X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fvfat%2Fnamei.c;h=9a8f48bae95626b739b34867713fc2d7d367f649;hb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;hp=85f78ea875481bc3a0fcda0df1f171a8b67bfda2;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index 85f78ea87..9a8f48bae 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -11,8 +11,8 @@ * * Short name translation 1999, 2001 by Wolfram Pienkoss * - * Support Multibyte character and cleanup by - * OGAWA Hirofumi + * Support Multibyte characters and cleanup by + * OGAWA Hirofumi */ #include @@ -25,33 +25,6 @@ #include #include -static int vfat_hashi(struct dentry *parent, struct qstr *qstr); -static int vfat_hash(struct dentry *parent, struct qstr *qstr); -static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b); -static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b); -static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd); - -static struct dentry_operations vfat_dentry_ops[4] = { - { - .d_hash = vfat_hashi, - .d_compare = vfat_cmpi, - }, - { - .d_revalidate = vfat_revalidate, - .d_hash = vfat_hashi, - .d_compare = vfat_cmpi, - }, - { - .d_hash = vfat_hash, - .d_compare = vfat_cmp, - }, - { - .d_revalidate = vfat_revalidate, - .d_hash = vfat_hash, - .d_compare = vfat_cmp, - } -}; - static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd) { int ret = 1; @@ -73,41 +46,13 @@ static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd) return ret; } -static inline unsigned char -vfat_tolower(struct nls_table *t, unsigned char c) -{ - unsigned char nc = t->charset2lower[c]; - - return nc ? nc : c; -} - -static inline unsigned char -vfat_toupper(struct nls_table *t, unsigned char c) -{ - unsigned char nc = t->charset2upper[c]; - - return nc ? nc : c; -} - -static inline int -vfat_strnicmp(struct nls_table *t, const unsigned char *s1, - const unsigned char *s2, int len) -{ - while(len--) - if (vfat_tolower(t, *s1++) != vfat_tolower(t, *s2++)) - return 1; - - return 0; -} - /* returns the length of a struct qstr, ignoring trailing dots */ static unsigned int vfat_striptail_len(struct qstr *qstr) { unsigned int len = qstr->len; - while (len && qstr->name[len-1] == '.') + while (len && qstr->name[len - 1] == '.') len--; - return len; } @@ -120,7 +65,6 @@ static unsigned int vfat_striptail_len(struct qstr *qstr) static int vfat_hash(struct dentry *dentry, struct qstr *qstr) { qstr->hash = full_name_hash(qstr->name, vfat_striptail_len(qstr)); - return 0; } @@ -142,7 +86,7 @@ static int vfat_hashi(struct dentry *dentry, struct qstr *qstr) hash = init_name_hash(); while (len--) - hash = partial_name_hash(vfat_tolower(t, *name++), hash); + hash = partial_name_hash(nls_tolower(t, *name++), hash); qstr->hash = end_name_hash(hash); return 0; @@ -160,7 +104,7 @@ static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b) alen = vfat_striptail_len(a); blen = vfat_striptail_len(b); if (alen == blen) { - if (vfat_strnicmp(t, a->name, b->name, alen) == 0) + if (nls_strnicmp(t, a->name, b->name, alen) == 0) return 0; } return 1; @@ -183,89 +127,78 @@ static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b) return 1; } -/* Characters that are undesirable in an MS-DOS file name */ - -static wchar_t bad_chars[] = { - /* `*' `?' `<' `>' `|' `"' `:' `/' */ - 0x002A, 0x003F, 0x003C, 0x003E, 0x007C, 0x0022, 0x003A, 0x002F, - /* `\' */ - 0x005C, 0, +static struct dentry_operations vfat_dentry_ops[4] = { + { + .d_hash = vfat_hashi, + .d_compare = vfat_cmpi, + }, + { + .d_revalidate = vfat_revalidate, + .d_hash = vfat_hashi, + .d_compare = vfat_cmpi, + }, + { + .d_hash = vfat_hash, + .d_compare = vfat_cmp, + }, + { + .d_revalidate = vfat_revalidate, + .d_hash = vfat_hash, + .d_compare = vfat_cmp, + } }; -#define IS_BADCHAR(uni) (vfat_unistrchr(bad_chars, (uni)) != NULL) -static wchar_t replace_chars[] = { - /* `[' `]' `;' `,' `+' `=' */ - 0x005B, 0x005D, 0x003B, 0x002C, 0x002B, 0x003D, 0, -}; -#define IS_REPLACECHAR(uni) (vfat_unistrchr(replace_chars, (uni)) != NULL) +/* Characters that are undesirable in an MS-DOS file name */ -static wchar_t skip_chars[] = { - /* `.' ` ' */ - 0x002E, 0x0020, 0, -}; -#define IS_SKIPCHAR(uni) \ - ((wchar_t)(uni) == skip_chars[0] || (wchar_t)(uni) == skip_chars[1]) +static inline wchar_t vfat_bad_char(wchar_t w) +{ + return (w < 0x0020) + || (w == '*') || (w == '?') || (w == '<') || (w == '>') + || (w == '|') || (w == '"') || (w == ':') || (w == '/') + || (w == '\\'); +} -static inline wchar_t *vfat_unistrchr(const wchar_t *s, const wchar_t c) +static inline wchar_t vfat_replace_char(wchar_t w) { - for(; *s != c; ++s) - if (*s == 0) - return NULL; - return (wchar_t *) s; + return (w == '[') || (w == ']') || (w == ';') || (w == ',') + || (w == '+') || (w == '='); +} + +static wchar_t vfat_skip_char(wchar_t w) +{ + return (w == '.') || (w == ' '); } static inline int vfat_is_used_badchars(const wchar_t *s, int len) { int i; - + for (i = 0; i < len; i++) - if (s[i] < 0x0020 || IS_BADCHAR(s[i])) + if (vfat_bad_char(s[i])) return -EINVAL; return 0; } static int vfat_valid_longname(const unsigned char *name, unsigned int len) { - if (len && name[len-1] == ' ') - return 0; + if (name[len - 1] == ' ') + return -EINVAL; if (len >= 256) - return 0; - - /* MS-DOS "device special files" */ - if (len == 3 || (len > 3 && name[3] == '.')) { /* basename == 3 */ - if (!strnicmp(name, "aux", 3) || - !strnicmp(name, "con", 3) || - !strnicmp(name, "nul", 3) || - !strnicmp(name, "prn", 3)) - return 0; - } - if (len == 4 || (len > 4 && name[4] == '.')) { /* basename == 4 */ - /* "com1", "com2", ... */ - if ('1' <= name[3] && name[3] <= '9') { - if (!strnicmp(name, "com", 3) || - !strnicmp(name, "lpt", 3)) - return 0; - } - } - - return 1; + return -ENAMETOOLONG; + return 0; } static int vfat_find_form(struct inode *dir, unsigned char *name) { - struct msdos_dir_entry *de; - struct buffer_head *bh = NULL; - loff_t i_pos; - int res; - - res = fat_scan(dir, name, &bh, &de, &i_pos); - brelse(bh); - if (res < 0) + struct fat_slot_info sinfo; + int err = fat_scan(dir, name, &sinfo); + if (err) return -ENOENT; + brelse(sinfo.bh); return 0; } -/* +/* * 1) Valid characters for the 8.3 format alias are any combination of * letters, uppercase alphabets, digits, any of the * following special characters: @@ -275,11 +208,11 @@ static int vfat_find_form(struct inode *dir, unsigned char *name) * WinNT's Extension: * File name and extension name is contain uppercase/lowercase * only. And it is expressed by CASE_LOWER_BASE and CASE_LOWER_EXT. - * + * * 2) File name is 8.3 format, but it contain the uppercase and * lowercase char, muliti bytes char, etc. In this case numtail is not * added, but Longfilename is stored. - * + * * 3) When the one except for the above, or the following special * character are contained: * . [ ] ; , + = @@ -296,38 +229,22 @@ struct shortname_info { (x)->valid = 1; \ } while (0) -static inline unsigned char -shortname_info_to_lcase(struct shortname_info *base, - struct shortname_info *ext) -{ - unsigned char lcase = 0; - - if (base->valid && ext->valid) { - if (!base->upper && base->lower && (ext->lower || ext->upper)) - lcase |= CASE_LOWER_BASE; - if (!ext->upper && ext->lower && (base->lower || base->upper)) - lcase |= CASE_LOWER_EXT; - } - - return lcase; -} - static inline int to_shortname_char(struct nls_table *nls, - unsigned char *buf, int buf_size, wchar_t *src, - struct shortname_info *info) + unsigned char *buf, int buf_size, + wchar_t *src, struct shortname_info *info) { int len; - if (IS_SKIPCHAR(*src)) { + if (vfat_skip_char(*src)) { info->valid = 0; return 0; } - if (IS_REPLACECHAR(*src)) { + if (vfat_replace_char(*src)) { info->valid = 0; buf[0] = '_'; return 1; } - + len = nls->uni2char(*src, buf, buf_size); if (len <= 0) { info->valid = 0; @@ -341,7 +258,7 @@ static inline int to_shortname_char(struct nls_table *nls, info->upper = 0; } - buf[0] = vfat_toupper(nls, buf[0]); + buf[0] = nls_toupper(nls, buf[0]); if (isalpha(buf[0])) { if (buf[0] == prev) info->lower = 0; @@ -352,7 +269,7 @@ static inline int to_shortname_char(struct nls_table *nls, info->lower = 0; info->upper = 0; } - + return len; } @@ -366,6 +283,7 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, wchar_t *uname, int ulen, unsigned char *name_res, unsigned char *lcase) { + struct fat_mount_options *opts = &MSDOS_SB(dir->i_sb)->options; wchar_t *ip, *ext_start, *end, *name_start; unsigned char base[9], ext[4], buf[8], *p; unsigned char charbuf[NLS_MAX_CHARSET_SIZE]; @@ -373,7 +291,6 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, int sz = 0, extlen, baselen, i, numtail_baselen, numtail2_baselen; int is_shortname; struct shortname_info base_info, ext_info; - unsigned short opt_shortname = MSDOS_SB(dir->i_sb)->options.shortname; is_shortname = 1; INIT_SHORTNAME_INFO(&base_info); @@ -382,7 +299,7 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, /* Now, we need to create a shortname from the long name */ ext_start = end = &uname[ulen]; while (--ext_start >= uname) { - if (*ext_start == 0x002E) { /* is `.' */ + if (*ext_start == 0x002E) { /* is `.' */ if (ext_start == end - 1) { sz = ulen; ext_start = NULL; @@ -402,7 +319,7 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, */ name_start = &uname[0]; while (name_start < ext_start) { - if (!IS_SKIPCHAR(*name_start)) + if (!vfat_skip_char(*name_start)) break; name_start++; } @@ -411,7 +328,7 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, ext_start++; } else { sz = ulen; - ext_start=NULL; + ext_start = NULL; } } @@ -427,7 +344,7 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, numtail2_baselen = baselen; if (baselen < 6 && (baselen + chl) > 6) numtail_baselen = baselen; - for (chi = 0; chi < chl; chi++){ + for (chi = 0; chi < chl; chi++) { *p++ = charbuf[chi]; baselen++; if (baselen >= 8) @@ -486,13 +403,15 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, if (vfat_find_form(dir, name_res) == 0) return -EEXIST; - if (opt_shortname & VFAT_SFN_CREATE_WIN95) { + if (opts->shortname & VFAT_SFN_CREATE_WIN95) { return (base_info.upper && ext_info.upper); - } else if (opt_shortname & VFAT_SFN_CREATE_WINNT) { - if ((base_info.upper || base_info.lower) - && (ext_info.upper || ext_info.lower)) { - *lcase = shortname_info_to_lcase(&base_info, - &ext_info); + } else if (opts->shortname & VFAT_SFN_CREATE_WINNT) { + if ((base_info.upper || base_info.lower) && + (ext_info.upper || ext_info.lower)) { + if (!base_info.upper && base_info.lower) + *lcase |= CASE_LOWER_BASE; + if (!ext_info.upper && ext_info.lower) + *lcase |= CASE_LOWER_EXT; return 1; } return 0; @@ -500,8 +419,8 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, BUG(); } } - - if (MSDOS_SB(dir->i_sb)->options.numtail == 0) + + if (opts->numtail == 0) if (vfat_find_form(dir, name_res) < 0) return 0; @@ -513,25 +432,25 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, * values for part of the base. */ - if (baselen>6) { + if (baselen > 6) { baselen = numtail_baselen; name_res[7] = ' '; } name_res[baselen] = '~'; for (i = 1; i < 10; i++) { - name_res[baselen+1] = i + '0'; + name_res[baselen + 1] = i + '0'; if (vfat_find_form(dir, name_res) < 0) return 0; } i = jiffies & 0xffff; sz = (jiffies >> 16) & 0x7; - if (baselen>2) { + if (baselen > 2) { baselen = numtail2_baselen; name_res[7] = ' '; } - name_res[baselen+4] = '~'; - name_res[baselen+5] = '1' + sz; + name_res[baselen + 4] = '~'; + name_res[baselen + 5] = '1' + sz; while (1) { sprintf(buf, "%04X", i); memcpy(&name_res[baselen], buf, 4); @@ -564,13 +483,14 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname, * We stripped '.'s before and set len appropriately, * but utf8_mbstowcs doesn't care about len */ - *outlen -= (name_len-len); + *outlen -= (name_len - len); op = &outname[*outlen * sizeof(wchar_t)]; } else { if (nls) { for (i = 0, ip = name, op = outname, *outlen = 0; - i < len && *outlen <= 260; *outlen += 1) + i < len && *outlen <= 260; + *outlen += 1) { if (escape && (*ip == ':')) { if (i > len - 5) @@ -598,7 +518,7 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname, ip += 5; i += 5; } else { - if ((charlen = nls->char2uni(ip, len-i, (wchar_t *)op)) < 0) + if ((charlen = nls->char2uni(ip, len - i, (wchar_t *)op)) < 0) return -EINVAL; ip += charlen; i += charlen; @@ -607,7 +527,8 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname, } } else { for (i = 0, ip = name, op = outname, *outlen = 0; - i < len && *outlen <= 260; i++, *outlen += 1) + i < len && *outlen <= 260; + i++, *outlen += 1) { *op++ = *ip++; *op++ = 0; @@ -636,8 +557,9 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname, } static int vfat_build_slots(struct inode *dir, const unsigned char *name, - int len, struct msdos_dir_slot *ds, - int *slots, int is_dir) + int len, int is_dir, int cluster, + struct timespec *ts, + struct msdos_dir_slot *slots, int *nr_slots) { struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb); struct fat_mount_options *opts = &sbi->options; @@ -647,201 +569,154 @@ static int vfat_build_slots(struct inode *dir, const unsigned char *name, unsigned char cksum, lcase; unsigned char msdos_name[MSDOS_NAME]; wchar_t *uname; - int res, slot, ulen, usize, i; + __le16 time, date; + int err, ulen, usize, i; loff_t offset; - *slots = 0; - if (!vfat_valid_longname(name, len)) - return -EINVAL; + *nr_slots = 0; + err = vfat_valid_longname(name, len); + if (err) + return err; - if(!(page = __get_free_page(GFP_KERNEL))) + page = __get_free_page(GFP_KERNEL); + if (!page) return -ENOMEM; uname = (wchar_t *)page; - res = xlate_to_uni(name, len, (unsigned char *)uname, &ulen, &usize, + err = xlate_to_uni(name, len, (unsigned char *)uname, &ulen, &usize, opts->unicode_xlate, opts->utf8, sbi->nls_io); - if (res < 0) + if (err) goto out_free; - res = vfat_is_used_badchars(uname, ulen); - if (res < 0) + err = vfat_is_used_badchars(uname, ulen); + if (err) goto out_free; - res = vfat_create_shortname(dir, sbi->nls_disk, uname, ulen, + err = vfat_create_shortname(dir, sbi->nls_disk, uname, ulen, msdos_name, &lcase); - if (res < 0) + if (err < 0) goto out_free; - else if (res == 1) { - de = (struct msdos_dir_entry *)ds; - res = 0; + else if (err == 1) { + de = (struct msdos_dir_entry *)slots; + err = 0; goto shortname; } /* build the entry of long file name */ - *slots = usize / 13; - for (cksum = i = 0; i < 11; i++) { - cksum = (((cksum&1)<<7)|((cksum&0xfe)>>1)) + msdos_name[i]; - } + cksum = fat_checksum(msdos_name); - for (ps = ds, slot = *slots; slot > 0; slot--, ps++) { - ps->id = slot; + *nr_slots = usize / 13; + for (ps = slots, i = *nr_slots; i > 0; i--, ps++) { + ps->id = i; ps->attr = ATTR_EXT; ps->reserved = 0; ps->alias_checksum = cksum; ps->start = 0; - offset = (slot - 1) * 13; + offset = (i - 1) * 13; fatwchar_to16(ps->name0_4, uname + offset, 5); fatwchar_to16(ps->name5_10, uname + offset + 5, 6); fatwchar_to16(ps->name11_12, uname + offset + 11, 2); } - ds[0].id |= 0x40; - de = (struct msdos_dir_entry *) ps; + slots[0].id |= 0x40; + de = (struct msdos_dir_entry *)ps; shortname: /* build the entry of 8.3 alias name */ - (*slots)++; + (*nr_slots)++; memcpy(de->name, msdos_name, MSDOS_NAME); de->attr = is_dir ? ATTR_DIR : ATTR_ARCH; de->lcase = lcase; - de->adate = de->cdate = de->date = 0; - de->ctime_ms = de->ctime = de->time = 0; - de->start = 0; - de->starthi = 0; + fat_date_unix2dos(ts->tv_sec, &time, &date); + de->time = de->ctime = time; + de->date = de->cdate = de->adate = date; + de->ctime_cs = 0; + de->start = cpu_to_le16(cluster); + de->starthi = cpu_to_le16(cluster >> 16); de->size = 0; - out_free: free_page(page); - return res; + return err; } -static int vfat_add_entry(struct inode *dir,struct qstr* qname, - int is_dir, struct vfat_slot_info *sinfo_out, - struct buffer_head **bh, struct msdos_dir_entry **de) +static int vfat_add_entry(struct inode *dir, struct qstr *qname, int is_dir, + int cluster, struct timespec *ts, + struct fat_slot_info *sinfo) { - struct msdos_dir_slot *dir_slots; - loff_t offset; - int res, slots, slot; + struct msdos_dir_slot *slots; unsigned int len; - struct msdos_dir_entry *dummy_de; - struct buffer_head *dummy_bh; - loff_t dummy_i_pos; + int err, nr_slots; len = vfat_striptail_len(qname); if (len == 0) return -ENOENT; - dir_slots = - kmalloc(sizeof(struct msdos_dir_slot) * MSDOS_SLOTS, GFP_KERNEL); - if (dir_slots == NULL) + slots = kmalloc(sizeof(*slots) * MSDOS_SLOTS, GFP_KERNEL); + if (slots == NULL) return -ENOMEM; - res = vfat_build_slots(dir, qname->name, len, - dir_slots, &slots, is_dir); - if (res < 0) + err = vfat_build_slots(dir, qname->name, len, is_dir, cluster, ts, + slots, &nr_slots); + if (err) goto cleanup; - /* build the empty directory entry of number of slots */ - offset = fat_add_entries(dir, slots, &dummy_bh, &dummy_de, &dummy_i_pos); - if (offset < 0) { - res = offset; + err = fat_add_entries(dir, slots, nr_slots, sinfo); + if (err) goto cleanup; - } - brelse(dummy_bh); - - /* Now create the new entry */ - *bh = NULL; - for (slot = 0; slot < slots; slot++) { - if (fat_get_entry(dir, &offset, bh, de, &sinfo_out->i_pos) < 0) { - res = -EIO; - goto cleanup; - } - memcpy(*de, dir_slots + slot, sizeof(struct msdos_dir_slot)); - mark_buffer_dirty(*bh); - } - res = 0; /* update timestamp */ - dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME; - mark_inode_dirty(dir); - - fat_date_unix2dos(dir->i_mtime.tv_sec, &(*de)->time, &(*de)->date); - (*de)->ctime = (*de)->time; - (*de)->adate = (*de)->cdate = (*de)->date; - - mark_buffer_dirty(*bh); - - /* slots can't be less than 1 */ - sinfo_out->long_slots = slots - 1; - sinfo_out->longname_offset = - offset - sizeof(struct msdos_dir_slot) * slots; - + dir->i_ctime = dir->i_mtime = dir->i_atime = *ts; + if (IS_DIRSYNC(dir)) + (void)fat_sync_inode(dir); + else + mark_inode_dirty(dir); cleanup: - kfree(dir_slots); - return res; + kfree(slots); + return err; } -static int vfat_find(struct inode *dir,struct qstr* qname, - struct vfat_slot_info *sinfo, struct buffer_head **last_bh, - struct msdos_dir_entry **last_de) +static int vfat_find(struct inode *dir, struct qstr *qname, + struct fat_slot_info *sinfo) { - struct super_block *sb = dir->i_sb; - loff_t offset; - unsigned int len; - int res; - - len = vfat_striptail_len(qname); + unsigned int len = vfat_striptail_len(qname); if (len == 0) return -ENOENT; - - res = fat_search_long(dir, qname->name, len, - (MSDOS_SB(sb)->options.name_check != 's'), - &offset, &sinfo->longname_offset); - if (res>0) { - sinfo->long_slots = res-1; - if (fat_get_entry(dir,&offset,last_bh,last_de,&sinfo->i_pos)>=0) - return 0; - res = -EIO; - } - return res ? res : -ENOENT; + return fat_search_long(dir, qname->name, len, sinfo); } static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd) + struct nameidata *nd) { - int res; - struct vfat_slot_info sinfo; - struct inode *inode; + struct super_block *sb = dir->i_sb; + struct fat_slot_info sinfo; + struct inode *inode = NULL; struct dentry *alias; - struct buffer_head *bh = NULL; - struct msdos_dir_entry *de; - int table; - + int err, table; + lock_kernel(); - table = (MSDOS_SB(dir->i_sb)->options.name_check == 's') ? 2 : 0; + table = (MSDOS_SB(sb)->options.name_check == 's') ? 2 : 0; dentry->d_op = &vfat_dentry_ops[table]; - inode = NULL; - res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de); - if (res < 0) { + err = vfat_find(dir, &dentry->d_name, &sinfo); + if (err) { table++; goto error; } - inode = fat_build_inode(dir->i_sb, de, sinfo.i_pos, &res); - brelse(bh); - if (res) { + inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); + brelse(sinfo.bh); + if (IS_ERR(inode)) { unlock_kernel(); - return ERR_PTR(res); + return ERR_PTR(PTR_ERR(inode)); } alias = d_find_alias(inode); if (alias) { - if (d_invalidate(alias)==0) + if (d_invalidate(alias) == 0) dput(alias); else { iput(inode); unlock_kernel(); return alias; } - + } error: unlock_kernel(); @@ -855,243 +730,271 @@ error: return dentry; } -static int vfat_create(struct inode *dir, struct dentry* dentry, int mode, - struct nameidata *nd) +static int vfat_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) { struct super_block *sb = dir->i_sb; - struct inode *inode = NULL; - struct buffer_head *bh = NULL; - struct msdos_dir_entry *de; - struct vfat_slot_info sinfo; - int res; + struct inode *inode; + struct fat_slot_info sinfo; + struct timespec ts; + int err; lock_kernel(); - res = vfat_add_entry(dir, &dentry->d_name, 0, &sinfo, &bh, &de); - if (res < 0) + + ts = CURRENT_TIME_SEC; + err = vfat_add_entry(dir, &dentry->d_name, 0, 0, &ts, &sinfo); + if (err) goto out; - inode = fat_build_inode(sb, de, sinfo.i_pos, &res); - brelse(bh); - if (!inode) + dir->i_version++; + + inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); + brelse(sinfo.bh); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); goto out; - res = 0; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); + } inode->i_version++; - dir->i_version++; + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + dentry->d_time = dentry->d_parent->d_inode->i_version; - d_instantiate(dentry,inode); + d_instantiate(dentry, inode); out: unlock_kernel(); - return res; + return err; } -static void vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo, - struct buffer_head *bh, struct msdos_dir_entry *de) +static int vfat_rmdir(struct inode *dir, struct dentry *dentry) { - loff_t offset, i_pos; - int i; - - /* remove the shortname */ - dir->i_mtime = dir->i_atime = CURRENT_TIME; - dir->i_version++; - mark_inode_dirty(dir); - de->name[0] = DELETED_FLAG; - mark_buffer_dirty(bh); - /* remove the longname */ - offset = sinfo->longname_offset; de = NULL; - for (i = sinfo->long_slots; i > 0; --i) { - if (fat_get_entry(dir, &offset, &bh, &de, &i_pos) < 0) - continue; - de->name[0] = DELETED_FLAG; - de->attr = ATTR_NONE; - mark_buffer_dirty(bh); - } - brelse(bh); -} - -static int vfat_rmdir(struct inode *dir, struct dentry* dentry) -{ - int res; - struct vfat_slot_info sinfo; - struct buffer_head *bh = NULL; - struct msdos_dir_entry *de; + struct inode *inode = dentry->d_inode; + struct fat_slot_info sinfo; + int err; lock_kernel(); - res = fat_dir_empty(dentry->d_inode); - if (res) + + err = fat_dir_empty(inode); + if (err) + goto out; + err = vfat_find(dir, &dentry->d_name, &sinfo); + if (err) goto out; - res = vfat_find(dir,&dentry->d_name,&sinfo, &bh, &de); - if (res < 0) + err = fat_remove_entries(dir, &sinfo); /* and releases bh */ + if (err) goto out; - res = 0; - dentry->d_inode->i_nlink = 0; - dentry->d_inode->i_mtime = dentry->d_inode->i_atime = CURRENT_TIME; - fat_detach(dentry->d_inode); - mark_inode_dirty(dentry->d_inode); - /* releases bh */ - vfat_remove_entry(dir,&sinfo,bh,de); dir->i_nlink--; + + inode->i_nlink = 0; + inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC; + fat_detach(inode); out: unlock_kernel(); - return res; + + return err; } static int vfat_unlink(struct inode *dir, struct dentry *dentry) { - int res; - struct vfat_slot_info sinfo; - struct buffer_head *bh = NULL; - struct msdos_dir_entry *de; + struct inode *inode = dentry->d_inode; + struct fat_slot_info sinfo; + int err; lock_kernel(); - res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de); - if (res < 0) + + err = vfat_find(dir, &dentry->d_name, &sinfo); + if (err) goto out; - dentry->d_inode->i_nlink = 0; - dentry->d_inode->i_mtime = dentry->d_inode->i_atime = CURRENT_TIME; - fat_detach(dentry->d_inode); - mark_inode_dirty(dentry->d_inode); - /* releases bh */ - vfat_remove_entry(dir,&sinfo,bh,de); + + err = fat_remove_entries(dir, &sinfo); /* and releases bh */ + if (err) + goto out; + inode->i_nlink = 0; + inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC; + fat_detach(inode); out: unlock_kernel(); - return res; + return err; } -static int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode) +static int vfat_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct super_block *sb = dir->i_sb; - struct inode *inode = NULL; - struct vfat_slot_info sinfo; - struct buffer_head *bh = NULL; - struct msdos_dir_entry *de; - int res; + struct inode *inode; + struct fat_slot_info sinfo; + struct timespec ts; + int err, cluster; lock_kernel(); - res = vfat_add_entry(dir, &dentry->d_name, 1, &sinfo, &bh, &de); - if (res < 0) + + ts = CURRENT_TIME_SEC; + cluster = fat_alloc_new_dir(dir, &ts); + if (cluster < 0) { + err = cluster; goto out; - inode = fat_build_inode(sb, de, sinfo.i_pos, &res); - if (!inode) - goto out_brelse; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); - inode->i_version++; + } + err = vfat_add_entry(dir, &dentry->d_name, 1, cluster, &ts, &sinfo); + if (err) + goto out_free; dir->i_version++; dir->i_nlink++; - inode->i_nlink = 2; /* no need to mark them dirty */ - res = fat_new_dir(inode, dir, 1); - if (res < 0) - goto mkdir_failed; + + inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); + brelse(sinfo.bh); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + /* the directory was completed, just return a error */ + goto out; + } + inode->i_version++; + inode->i_nlink = 2; + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + dentry->d_time = dentry->d_parent->d_inode->i_version; - d_instantiate(dentry,inode); -out_brelse: - brelse(bh); -out: + d_instantiate(dentry, inode); + unlock_kernel(); - return res; + return 0; -mkdir_failed: - inode->i_nlink = 0; - inode->i_mtime = inode->i_atime = CURRENT_TIME; - fat_detach(inode); - mark_inode_dirty(inode); - /* releases bh */ - vfat_remove_entry(dir,&sinfo,bh,de); - iput(inode); - dir->i_nlink--; - goto out; +out_free: + fat_free_clusters(dir, cluster); +out: + unlock_kernel(); + return err; } - + static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) + struct inode *new_dir, struct dentry *new_dentry) { - struct buffer_head *old_bh,*new_bh,*dotdot_bh; - struct msdos_dir_entry *old_de,*new_de,*dotdot_de; - loff_t dotdot_i_pos; + struct buffer_head *dotdot_bh; + struct msdos_dir_entry *dotdot_de; struct inode *old_inode, *new_inode; - int res, is_dir; - struct vfat_slot_info old_sinfo,sinfo; + struct fat_slot_info old_sinfo, sinfo; + struct timespec ts; + loff_t dotdot_i_pos, new_i_pos; + int err, is_dir, update_dotdot, corrupt = 0; - old_bh = new_bh = dotdot_bh = NULL; + old_sinfo.bh = sinfo.bh = dotdot_bh = NULL; old_inode = old_dentry->d_inode; new_inode = new_dentry->d_inode; lock_kernel(); - res = vfat_find(old_dir,&old_dentry->d_name,&old_sinfo,&old_bh,&old_de); - if (res < 0) - goto rename_done; + err = vfat_find(old_dir, &old_dentry->d_name, &old_sinfo); + if (err) + goto out; is_dir = S_ISDIR(old_inode->i_mode); - - if (is_dir) { - if (fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh, - &dotdot_de, &dotdot_i_pos) < 0) { - res = -EIO; - goto rename_done; + update_dotdot = (is_dir && old_dir != new_dir); + if (update_dotdot) { + if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de, + &dotdot_i_pos) < 0) { + err = -EIO; + goto out; } } - if (new_dentry->d_inode) { - res = vfat_find(new_dir,&new_dentry->d_name,&sinfo,&new_bh, - &new_de); - if (res < 0 || MSDOS_I(new_inode)->i_pos != sinfo.i_pos) { - /* WTF??? Cry and fail. */ - printk(KERN_WARNING "vfat_rename: fs corrupted\n"); - goto rename_done; - } - + ts = CURRENT_TIME_SEC; + if (new_inode) { if (is_dir) { - res = fat_dir_empty(new_inode); - if (res) - goto rename_done; + err = fat_dir_empty(new_inode); + if (err) + goto out; } + new_i_pos = MSDOS_I(new_inode)->i_pos; fat_detach(new_inode); } else { - res = vfat_add_entry(new_dir,&new_dentry->d_name,is_dir,&sinfo, - &new_bh,&new_de); - if (res < 0) goto rename_done; + err = vfat_add_entry(new_dir, &new_dentry->d_name, is_dir, 0, + &ts, &sinfo); + if (err) + goto out; + new_i_pos = sinfo.i_pos; } - new_dir->i_version++; - /* releases old_bh */ - vfat_remove_entry(old_dir,&old_sinfo,old_bh,old_de); - old_bh=NULL; fat_detach(old_inode); - fat_attach(old_inode, sinfo.i_pos); - mark_inode_dirty(old_inode); - - old_dir->i_version++; - old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; - mark_inode_dirty(old_dir); - if (new_inode) { - new_inode->i_nlink--; - new_inode->i_ctime=CURRENT_TIME; - } - - if (is_dir) { + fat_attach(old_inode, new_i_pos); + if (IS_DIRSYNC(new_dir)) { + err = fat_sync_inode(old_inode); + if (err) + goto error_inode; + } else + mark_inode_dirty(old_inode); + + if (update_dotdot) { int start = MSDOS_I(new_dir)->i_logstart; - dotdot_de->start = CT_LE_W(start); - dotdot_de->starthi = CT_LE_W(start>>16); + dotdot_de->start = cpu_to_le16(start); + dotdot_de->starthi = cpu_to_le16(start >> 16); mark_buffer_dirty(dotdot_bh); - old_dir->i_nlink--; - if (new_inode) { - new_inode->i_nlink--; - } else { - new_dir->i_nlink++; - mark_inode_dirty(new_dir); + if (IS_DIRSYNC(new_dir)) { + err = sync_dirty_buffer(dotdot_bh); + if (err) + goto error_dotdot; } + old_dir->i_nlink--; + if (!new_inode) + new_dir->i_nlink++; } -rename_done: + err = fat_remove_entries(old_dir, &old_sinfo); /* and releases bh */ + old_sinfo.bh = NULL; + if (err) + goto error_dotdot; + old_dir->i_version++; + old_dir->i_ctime = old_dir->i_mtime = ts; + if (IS_DIRSYNC(old_dir)) + (void)fat_sync_inode(old_dir); + else + mark_inode_dirty(old_dir); + + if (new_inode) { + if (is_dir) + new_inode->i_nlink -= 2; + else + new_inode->i_nlink--; + new_inode->i_ctime = ts; + } +out: + brelse(sinfo.bh); brelse(dotdot_bh); - brelse(old_bh); - brelse(new_bh); + brelse(old_sinfo.bh); unlock_kernel(); - return res; + return err; + +error_dotdot: + /* data cluster is shared, serious corruption */ + corrupt = 1; + + if (update_dotdot) { + int start = MSDOS_I(old_dir)->i_logstart; + dotdot_de->start = cpu_to_le16(start); + dotdot_de->starthi = cpu_to_le16(start >> 16); + mark_buffer_dirty(dotdot_bh); + corrupt |= sync_dirty_buffer(dotdot_bh); + } +error_inode: + fat_detach(old_inode); + fat_attach(old_inode, old_sinfo.i_pos); + if (new_inode) { + fat_attach(new_inode, new_i_pos); + if (corrupt) + corrupt |= fat_sync_inode(new_inode); + } else { + /* + * If new entry was not sharing the data cluster, it + * shouldn't be serious corruption. + */ + int err2 = fat_remove_entries(new_dir, &sinfo); + if (corrupt) + corrupt |= err2; + sinfo.bh = NULL; + } + if (corrupt < 0) { + fat_fs_panic(new_dir->i_sb, + "%s: Filesystem corrupted (i_pos %lld)", + __FUNCTION__, sinfo.i_pos); + } + goto out; } static struct inode_operations vfat_dir_inode_operations = { @@ -1120,10 +1023,12 @@ static int vfat_fill_super(struct super_block *sb, void *data, int silent) return 0; } -static struct super_block *vfat_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int vfat_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, vfat_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, vfat_fill_super, + mnt); } static struct file_system_type vfat_fs_type = {