X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=fs%2Ffat%2Fdir.c;h=fdb7153718148a9d3052993aadcca466362bd009;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=993d50c0db39c65e690b3d6eee4cade9f49a1c91;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 993d50c0d..fdb715371 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -13,22 +13,15 @@ * Short name translation 1999, 2001 by Wolfram Pienkoss */ +#include #include #include #include #include #include #include - #include -struct file_operations fat_dir_operations = { - .read = generic_read_dir, - .readdir = fat_readdir, - .ioctl = fat_dir_ioctl, - .fsync = file_fsync, -}; - /* * Convert Unicode 16 to UTF8, translated Unicode, or ASCII. * If uni_xlate is enabled and we can't get a 1:1 conversion, use a @@ -39,9 +32,8 @@ struct file_operations fat_dir_operations = { * but ignore that right now. * Ahem... Stack smashing in ring 0 isn't fun. Fixed. */ -static int -uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate, - struct nls_table *nls) +static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate, + struct nls_table *nls) { wchar_t *ip, ec; unsigned char *op, nc; @@ -79,20 +71,6 @@ uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate, return (op - ascii); } -#if 0 -static void dump_de(struct msdos_dir_entry *de) -{ - int i; - unsigned char *p = (unsigned char *) de; - printk("["); - - for (i = 0; i < 32; i++, p++) { - printk("%02x ", *p); - } - printk("]\n"); -} -#endif - static inline int fat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni) { @@ -118,17 +96,17 @@ fat_short2lower_uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *un charlen = 1; } else if (charlen <= 1) { unsigned char nc = t->charset2lower[*c]; - + if (!nc) nc = *c; - + if ( (charlen = t->char2uni(&nc, 1, uni)) < 0) { *uni = 0x003f; /* a question mark */ charlen = 1; } } else *uni = wc; - + return charlen; } @@ -145,7 +123,7 @@ fat_shortname2uni(struct nls_table *nls, unsigned char *buf, int buf_size, else if (opt & VFAT_SFN_DISPLAY_WINNT) { if (lower) len = fat_short2lower_uni(nls, buf, buf_size, uni_buf); - else + else len = fat_short2uni(nls, buf, buf_size, uni_buf); } else len = fat_short2uni(nls, buf, buf_size, uni_buf); @@ -321,16 +299,30 @@ EODir: return res; } +EXPORT_SYMBOL(fat_search_long); + +struct fat_ioctl_filldir_callback { + struct dirent __user *dirent; + int result; + /* for dir ioctl */ + const char *longname; + int long_len; + const char *shortname; + int short_len; +}; + static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, - filldir_t filldir, int shortnames, int both) + filldir_t filldir, int short_only, int both) { struct super_block *sb = inode->i_sb; struct buffer_head *bh; struct msdos_dir_entry *de; struct nls_table *nls_io = MSDOS_SB(sb)->nls_io; struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk; - wchar_t bufuname[14]; unsigned char long_slots; + const char *fill_name; + int fill_len; + wchar_t bufuname[14]; wchar_t *unicode = NULL; unsigned char c, work[8], bufname[56], *ptname = bufname; unsigned long lpos, dummy, *furrfu = &lpos; @@ -343,7 +335,7 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, int chi, chl, i, i2, j, last, last_u, dotoffset = 0; loff_t i_pos, cpos; int ret = 0; - + lock_kernel(); cpos = filp->f_pos; @@ -366,7 +358,7 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, goto out; } - bh = NULL; + bh = NULL; GetNew: long_slots = 0; if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos) == -1) @@ -393,8 +385,7 @@ GetNew: unsigned char alias_checksum; if (!unicode) { - unicode = (wchar_t *) - __get_free_page(GFP_KERNEL); + unicode = (wchar_t *)__get_free_page(GFP_KERNEL); if (!unicode) { filp->f_pos = cpos; brelse(bh); @@ -529,26 +520,35 @@ ParseLong: : uni16_to_x8(bufname, bufuname, uni_xlate, nls_io); } - if (!long_slots||shortnames) { - if (both) - bufname[i] = '\0'; - if (filldir(dirent, bufname, i, *furrfu, inum, - (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0) - goto FillFailed; - } else { - unsigned char longname[275]; + fill_name = bufname; + fill_len = i; + if (!short_only && long_slots) { + /* convert the unicode long name. 261 is maximum size + * of unicode buffer. (13 * slots + nul) */ + void *longname = unicode + 261; + int buf_size = PAGE_SIZE - (261 * sizeof(unicode[0])); int long_len = utf8 - ? utf8_wcstombs(longname, unicode, sizeof(longname)) - : uni16_to_x8(longname, unicode, uni_xlate, - nls_io); - if (both) { - memcpy(&longname[long_len+1], bufname, i); - long_len += i; + ? utf8_wcstombs(longname, unicode, buf_size) + : uni16_to_x8(longname, unicode, uni_xlate, nls_io); + + if (!both) { + fill_name = longname; + fill_len = long_len; + } else { + /* hack for fat_ioctl_filldir() */ + struct fat_ioctl_filldir_callback *p = dirent; + + p->longname = longname; + p->long_len = long_len; + p->shortname = bufname; + p->short_len = i; + fill_name = NULL; + fill_len = 0; } - if (filldir(dirent, longname, long_len, *furrfu, inum, - (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0) - goto FillFailed; } + if (filldir(dirent, fill_name, fill_len, *furrfu, inum, + (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0) + goto FillFailed; RecEnd: furrfu = &lpos; @@ -559,63 +559,61 @@ EODir: FillFailed: if (bh) brelse(bh); - if (unicode) { - free_page((unsigned long) unicode); - } + if (unicode) + free_page((unsigned long)unicode); out: unlock_kernel(); return ret; } -int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) +static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; return fat_readdirx(inode, filp, dirent, filldir, 0, 0); } -struct fat_ioctl_filldir_callback { - struct dirent __user *dirent; - int result; -}; - -static int fat_ioctl_filldir(void *__buf, const char * name, int name_len, +static int fat_ioctl_filldir(void *__buf, const char *name, int name_len, loff_t offset, ino_t ino, unsigned int d_type) { struct fat_ioctl_filldir_callback *buf = __buf; struct dirent __user *d1 = buf->dirent; struct dirent __user *d2 = d1 + 1; - int len, slen; - int dotdir; if (buf->result) return -EINVAL; buf->result++; - if ((name_len == 1 && name[0] == '.') || - (name_len == 2 && name[0] == '.' && name[1] == '.')) { - dotdir = 1; - len = name_len; - } else { - dotdir = 0; - len = strlen(name); - } - if (len != name_len) { - slen = name_len - len; - if (copy_to_user(d2->d_name, name, len) || - put_user(0, d2->d_name + len) || - put_user(len, &d2->d_reclen) || - put_user(ino, &d2->d_ino) || - put_user(offset, &d2->d_off) || - copy_to_user(d1->d_name, name+len+1, slen) || - put_user(0, d1->d_name+slen) || - put_user(slen, &d1->d_reclen)) - goto efault; - } else { + if (name != NULL) { + /* dirent has only short name */ + if (name_len >= sizeof(d1->d_name)) + name_len = sizeof(d1->d_name) - 1; + if (put_user(0, d2->d_name) || put_user(0, &d2->d_reclen) || - copy_to_user(d1->d_name, name, len) || - put_user(0, d1->d_name+len) || - put_user(len, &d1->d_reclen)) + copy_to_user(d1->d_name, name, name_len) || + put_user(0, d1->d_name + name_len) || + put_user(name_len, &d1->d_reclen)) + goto efault; + } else { + /* dirent has short and long name */ + const char *longname = buf->longname; + int long_len = buf->long_len; + const char *shortname = buf->shortname; + int short_len = buf->short_len; + + if (long_len >= sizeof(d1->d_name)) + long_len = sizeof(d1->d_name) - 1; + if (short_len >= sizeof(d1->d_name)) + short_len = sizeof(d1->d_name) - 1; + + if (copy_to_user(d2->d_name, longname, long_len) || + put_user(0, d2->d_name + long_len) || + put_user(long_len, &d2->d_reclen) || + put_user(ino, &d2->d_ino) || + put_user(offset, &d2->d_off) || + copy_to_user(d1->d_name, shortname, short_len) || + put_user(0, d1->d_name + short_len) || + put_user(short_len, &d1->d_reclen)) goto efault; } return 0; @@ -624,20 +622,20 @@ efault: return -EFAULT; } -int fat_dir_ioctl(struct inode * inode, struct file * filp, +static int fat_dir_ioctl(struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) { struct fat_ioctl_filldir_callback buf; struct dirent __user *d1; - int ret, shortname, both; + int ret, short_only, both; switch (cmd) { case VFAT_IOCTL_READDIR_SHORT: - shortname = 1; - both = 1; + short_only = 1; + both = 0; break; case VFAT_IOCTL_READDIR_BOTH: - shortname = 0; + short_only = 0; both = 1; break; default: @@ -661,7 +659,7 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp, ret = -ENOENT; if (!IS_DEADDIR(inode)) { ret = fat_readdirx(inode, filp, &buf, fat_ioctl_filldir, - shortname, both); + short_only, both); } up(&inode->i_sem); if (ret >= 0) @@ -669,81 +667,12 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp, return ret; } -/* This assumes that size of cluster is above the 32*slots */ - -int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh, - struct msdos_dir_entry **de, loff_t *i_pos) -{ - struct super_block *sb = dir->i_sb; - loff_t offset, curr; - int row; - struct buffer_head *new_bh; - - offset = curr = 0; - *bh = NULL; - row = 0; - while (fat_get_entry(dir, &curr, bh, de, i_pos) > -1) { - /* check the maximum size of directory */ - if (curr >= FAT_MAX_DIR_SIZE) { - brelse(*bh); - return -ENOSPC; - } - - if (IS_FREE((*de)->name)) { - if (++row == slots) - return offset; - } else { - row = 0; - offset = curr; - } - } - if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32)) - return -ENOSPC; - new_bh = fat_extend_dir(dir); - if (IS_ERR(new_bh)) - return PTR_ERR(new_bh); - brelse(new_bh); - do { - fat_get_entry(dir, &curr, bh, de, i_pos); - } while (++row < slots); - - return offset; -} - -int fat_new_dir(struct inode *dir, struct inode *parent, int is_vfat) -{ - struct buffer_head *bh; - struct msdos_dir_entry *de; - __le16 date, time; - - bh = fat_extend_dir(dir); - if (IS_ERR(bh)) - return PTR_ERR(bh); - - /* zeroed out, so... */ - fat_date_unix2dos(dir->i_mtime.tv_sec,&time,&date); - de = (struct msdos_dir_entry*)&bh->b_data[0]; - memcpy(de[0].name,MSDOS_DOT,MSDOS_NAME); - memcpy(de[1].name,MSDOS_DOTDOT,MSDOS_NAME); - de[0].attr = de[1].attr = ATTR_DIR; - de[0].time = de[1].time = time; - de[0].date = de[1].date = date; - if (is_vfat) { /* extra timestamps */ - de[0].ctime = de[1].ctime = time; - de[0].adate = de[0].cdate = - de[1].adate = de[1].cdate = date; - } - de[0].start = CT_LE_W(MSDOS_I(dir)->i_logstart); - de[0].starthi = CT_LE_W(MSDOS_I(dir)->i_logstart>>16); - de[1].start = CT_LE_W(MSDOS_I(parent)->i_logstart); - de[1].starthi = CT_LE_W(MSDOS_I(parent)->i_logstart>>16); - mark_buffer_dirty(bh); - brelse(bh); - dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - mark_inode_dirty(dir); - - return 0; -} +struct file_operations fat_dir_operations = { + .read = generic_read_dir, + .readdir = fat_readdir, + .ioctl = fat_dir_ioctl, + .fsync = file_fsync, +}; static int fat_get_short_entry(struct inode *dir, loff_t *pos, struct buffer_head **bh, @@ -778,6 +707,8 @@ int fat_dir_empty(struct inode *dir) return result; } +EXPORT_SYMBOL(fat_dir_empty); + /* * fat_subdirs counts the number of sub-directories of dir. It can be run * on directories being created. @@ -817,3 +748,127 @@ int fat_scan(struct inode *dir, const unsigned char *name, } return -ENOENT; } + +EXPORT_SYMBOL(fat_scan); + +static struct buffer_head *fat_extend_dir(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *bh, *res = NULL; + int nr, sec_per_clus = MSDOS_SB(sb)->sec_per_clus; + sector_t sector, last_sector; + + if (MSDOS_SB(sb)->fat_bits != 32) { + if (inode->i_ino == MSDOS_ROOT_INO) + return ERR_PTR(-ENOSPC); + } + + nr = fat_add_cluster(inode); + if (nr < 0) + return ERR_PTR(nr); + + sector = fat_clus_to_blknr(MSDOS_SB(sb), nr); + last_sector = sector + sec_per_clus; + for ( ; sector < last_sector; sector++) { + if ((bh = sb_getblk(sb, sector))) { + memset(bh->b_data, 0, sb->s_blocksize); + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + if (!res) + res = bh; + else + brelse(bh); + } + } + if (res == NULL) + res = ERR_PTR(-EIO); + if (inode->i_size & (sb->s_blocksize - 1)) { + fat_fs_panic(sb, "Odd directory size"); + inode->i_size = (inode->i_size + sb->s_blocksize) + & ~((loff_t)sb->s_blocksize - 1); + } + inode->i_size += MSDOS_SB(sb)->cluster_size; + MSDOS_I(inode)->mmu_private += MSDOS_SB(sb)->cluster_size; + + return res; +} + +/* This assumes that size of cluster is above the 32*slots */ + +int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh, + struct msdos_dir_entry **de, loff_t *i_pos) +{ + struct super_block *sb = dir->i_sb; + loff_t offset, curr; + int row; + struct buffer_head *new_bh; + + offset = curr = 0; + *bh = NULL; + row = 0; + while (fat_get_entry(dir, &curr, bh, de, i_pos) > -1) { + /* check the maximum size of directory */ + if (curr >= FAT_MAX_DIR_SIZE) { + brelse(*bh); + return -ENOSPC; + } + + if (IS_FREE((*de)->name)) { + if (++row == slots) + return offset; + } else { + row = 0; + offset = curr; + } + } + if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32)) + return -ENOSPC; + new_bh = fat_extend_dir(dir); + if (IS_ERR(new_bh)) + return PTR_ERR(new_bh); + brelse(new_bh); + do { + fat_get_entry(dir, &curr, bh, de, i_pos); + } while (++row < slots); + + return offset; +} + +EXPORT_SYMBOL(fat_add_entries); + +int fat_new_dir(struct inode *dir, struct inode *parent, int is_vfat) +{ + struct buffer_head *bh; + struct msdos_dir_entry *de; + __le16 date, time; + + bh = fat_extend_dir(dir); + if (IS_ERR(bh)) + return PTR_ERR(bh); + + /* zeroed out, so... */ + fat_date_unix2dos(dir->i_mtime.tv_sec,&time,&date); + de = (struct msdos_dir_entry*)&bh->b_data[0]; + memcpy(de[0].name,MSDOS_DOT,MSDOS_NAME); + memcpy(de[1].name,MSDOS_DOTDOT,MSDOS_NAME); + de[0].attr = de[1].attr = ATTR_DIR; + de[0].time = de[1].time = time; + de[0].date = de[1].date = date; + if (is_vfat) { /* extra timestamps */ + de[0].ctime = de[1].ctime = time; + de[0].adate = de[0].cdate = + de[1].adate = de[1].cdate = date; + } + de[0].start = cpu_to_le16(MSDOS_I(dir)->i_logstart); + de[0].starthi = cpu_to_le16(MSDOS_I(dir)->i_logstart>>16); + de[1].start = cpu_to_le16(MSDOS_I(parent)->i_logstart); + de[1].starthi = cpu_to_le16(MSDOS_I(parent)->i_logstart>>16); + mark_buffer_dirty(bh); + brelse(bh); + dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; + mark_inode_dirty(dir); + + return 0; +} + +EXPORT_SYMBOL(fat_new_dir);