X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fext3%2Fnamei.c;h=329eae367988cfb3346bbb14b0e83fa6021632a4;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=6ed3d4ff2bd9f981e5b521c4506563d1b1618152;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 6ed3d4ff2..329eae367 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -36,6 +36,9 @@ #include #include #include +#include + +#include "namei.h" #include "xattr.h" #include "acl.h" @@ -71,9 +74,6 @@ static struct buffer_head *ext3_append(handle_t *handle, #define swap(x, y) do { typeof(x) z = x; x = y; y = z; } while (0) #endif -typedef struct { u32 v; } le_u32; -typedef struct { u16 v; } le_u16; - #ifdef DX_DEBUG #define dxtrace(command) command #else @@ -82,22 +82,22 @@ typedef struct { u16 v; } le_u16; struct fake_dirent { - /*le*/u32 inode; - /*le*/u16 rec_len; + __le32 inode; + __le16 rec_len; u8 name_len; u8 file_type; }; struct dx_countlimit { - le_u16 limit; - le_u16 count; + __le16 limit; + __le16 count; }; struct dx_entry { - le_u32 hash; - le_u32 block; + __le32 hash; + __le32 block; }; /* @@ -114,7 +114,7 @@ struct dx_root char dotdot_name[4]; struct dx_root_info { - le_u32 reserved_zero; + __le32 reserved_zero; u8 hash_version; u8 info_length; /* 8 */ u8 indirect_levels; @@ -184,42 +184,42 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry, static inline unsigned dx_get_block (struct dx_entry *entry) { - return le32_to_cpu(entry->block.v) & 0x00ffffff; + return le32_to_cpu(entry->block) & 0x00ffffff; } static inline void dx_set_block (struct dx_entry *entry, unsigned value) { - entry->block.v = cpu_to_le32(value); + entry->block = cpu_to_le32(value); } static inline unsigned dx_get_hash (struct dx_entry *entry) { - return le32_to_cpu(entry->hash.v); + return le32_to_cpu(entry->hash); } static inline void dx_set_hash (struct dx_entry *entry, unsigned value) { - entry->hash.v = cpu_to_le32(value); + entry->hash = cpu_to_le32(value); } static inline unsigned dx_get_count (struct dx_entry *entries) { - return le16_to_cpu(((struct dx_countlimit *) entries)->count.v); + return le16_to_cpu(((struct dx_countlimit *) entries)->count); } static inline unsigned dx_get_limit (struct dx_entry *entries) { - return le16_to_cpu(((struct dx_countlimit *) entries)->limit.v); + return le16_to_cpu(((struct dx_countlimit *) entries)->limit); } static inline void dx_set_count (struct dx_entry *entries, unsigned value) { - ((struct dx_countlimit *) entries)->count.v = cpu_to_le16(value); + ((struct dx_countlimit *) entries)->count = cpu_to_le16(value); } static inline void dx_set_limit (struct dx_entry *entries, unsigned value) { - ((struct dx_countlimit *) entries)->limit.v = cpu_to_le16(value); + ((struct dx_countlimit *) entries)->limit = cpu_to_le16(value); } static inline unsigned dx_root_limit (struct inode *dir, unsigned infosize) @@ -557,6 +557,8 @@ static int htree_dirblock_to_tree(struct file *dir_file, ((hinfo->hash == start_hash) && (hinfo->minor_hash < start_minor_hash))) continue; + if (de->inode == 0) + continue; if ((err = ext3_htree_store_dirent(dir_file, hinfo->hash, hinfo->minor_hash, de)) != 0) { brelse(bh); @@ -602,7 +604,7 @@ int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash, } hinfo.hash = start_hash; hinfo.minor_hash = 0; - frame = dx_probe(0, dir_file->f_dentry->d_inode, &hinfo, frames, &err); + frame = dx_probe(NULL, dir_file->f_dentry->d_inode, &hinfo, frames, &err); if (!frame) return err; @@ -611,10 +613,14 @@ int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash, de = (struct ext3_dir_entry_2 *) frames[0].bh->b_data; if ((err = ext3_htree_store_dirent(dir_file, 0, 0, de)) != 0) goto errout; + count++; + } + if (start_hash < 2 || (start_hash ==2 && start_minor_hash==0)) { + de = (struct ext3_dir_entry_2 *) frames[0].bh->b_data; de = ext3_next_entry(de); - if ((err = ext3_htree_store_dirent(dir_file, 0, 0, de)) != 0) + if ((err = ext3_htree_store_dirent(dir_file, 2, 0, de)) != 0) goto errout; - count += 2; + count++; } while (1) { @@ -672,6 +678,7 @@ static int dx_make_map (struct ext3_dir_entry_2 *de, int size, map_tail->hash = h.hash; map_tail->offs = (u32) ((char *) de - base); count++; + cond_resched(); } /* XXX: do we need to check rec_len == 0 case? -Chris */ de = (struct ext3_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); @@ -872,6 +879,8 @@ restart: wait_on_buffer(bh); if (!buffer_uptodate(bh)) { /* read error, skip block & hope for the best */ + ext3_error(sb, __FUNCTION__, "reading directory #%lu " + "offset %lu", dir->i_ino, block); brelse(bh); goto next; } @@ -926,8 +935,16 @@ static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry, struct inode *dir = dentry->d_parent->d_inode; sb = dir->i_sb; - if (!(frame = dx_probe (dentry, 0, &hinfo, frames, err))) - return NULL; + /* NFS may look up ".." - look at dx_root directory block */ + if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){ + if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err))) + return NULL; + } else { + frame = frames; + frame->bh = NULL; /* for dx_release() */ + frame->at = (struct dx_entry *)frames; /* hack for zero entry*/ + dx_set_block(frame->at, 0); /* dx_root block is 0 */ + } hash = hinfo.hash; do { block = dx_get_block(frame->at); @@ -952,7 +969,7 @@ static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry, brelse (bh); /* Check to see if we should continue to search */ retval = ext3_htree_next_block(dir, hash, frame, - frames, 0); + frames, NULL); if (retval < 0) { ext3_warning(sb, __FUNCTION__, "error reading index page in directory #%lu", @@ -988,11 +1005,9 @@ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, str if (!inode) return ERR_PTR(-EACCES); + vx_propagate_xid(nd, inode); } - if (inode) - return d_splice_alias(inode, dentry); - d_add(dentry, inode); - return NULL; + return d_splice_alias(inode, dentry); } @@ -1250,7 +1265,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, * happen is that the times are slightly out of date * and/or different from the directory change time. */ - dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; ext3_update_dx_flag(dir); dir->i_version++; ext3_mark_inode_dirty(handle, dir); @@ -1388,7 +1403,7 @@ static int ext3_add_entry (handle_t *handle, struct dentry *dentry, bh = ext3_bread(handle, dir, block, 0, &retval); if(!bh) return retval; - retval = add_dirent_to_buf(handle, dentry, inode, 0, bh); + retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh); if (retval != -ENOSPC) return retval; @@ -1425,7 +1440,7 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry, struct ext3_dir_entry_2 *de; int err; - frame = dx_probe(dentry, 0, &hinfo, frames, &err); + frame = dx_probe(dentry, NULL, &hinfo, frames, &err); if (!frame) return err; entries = frame->entries; @@ -1439,9 +1454,9 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry, if (err) goto journal_error; - err = add_dirent_to_buf(handle, dentry, inode, 0, bh); + err = add_dirent_to_buf(handle, dentry, inode, NULL, bh); if (err != -ENOSPC) { - bh = 0; + bh = NULL; goto cleanup; } @@ -1460,7 +1475,7 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry, if (levels && (dx_get_count(frames->entries) == dx_get_limit(frames->entries))) { ext3_warning(sb, __FUNCTION__, - "Directory index full!\n"); + "Directory index full!"); err = -ENOSPC; goto cleanup; } @@ -1533,7 +1548,7 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry, if (!de) goto cleanup; err = add_dirent_to_buf(handle, dentry, inode, de, bh); - bh = 0; + bh = NULL; goto cleanup; journal_error: @@ -1628,11 +1643,12 @@ static int ext3_create (struct inode * dir, struct dentry * dentry, int mode, { handle_t *handle; struct inode * inode; - int err; + int err, retries = 0; - handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + +retry: + handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 + - 2*EXT3_QUOTA_INIT_BLOCKS); + 2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -1648,6 +1664,8 @@ static int ext3_create (struct inode * dir, struct dentry * dentry, int mode, err = ext3_add_nondir(handle, dentry, inode); } ext3_journal_stop(handle); + if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries)) + goto retry; return err; } @@ -1656,14 +1674,15 @@ static int ext3_mknod (struct inode * dir, struct dentry *dentry, { handle_t *handle; struct inode *inode; - int err; + int err, retries = 0; if (!new_valid_dev(rdev)) return -EINVAL; - handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + +retry: + handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 + - 2*EXT3_QUOTA_INIT_BLOCKS); + 2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -1680,6 +1699,8 @@ static int ext3_mknod (struct inode * dir, struct dentry *dentry, err = ext3_add_nondir(handle, dentry, inode); } ext3_journal_stop(handle); + if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries)) + goto retry; return err; } @@ -1689,14 +1710,15 @@ static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode) struct inode * inode; struct buffer_head * dir_block; struct ext3_dir_entry_2 * de; - int err; + int err, retries = 0; if (dir->i_nlink >= EXT3_LINK_MAX) return -EMLINK; - handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + +retry: + handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 + - 2*EXT3_QUOTA_INIT_BLOCKS); + 2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -1751,6 +1773,8 @@ static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode) d_instantiate(dentry, inode); out_stop: ext3_journal_stop(handle); + if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries)) + goto retry; return err; } @@ -1763,14 +1787,19 @@ static int empty_dir (struct inode * inode) struct buffer_head * bh; struct ext3_dir_entry_2 * de, * de1; struct super_block * sb; - int err; + int err = 0; sb = inode->i_sb; if (inode->i_size < EXT3_DIR_REC_LEN(1) + EXT3_DIR_REC_LEN(2) || !(bh = ext3_bread (NULL, inode, 0, 0, &err))) { - ext3_warning (inode->i_sb, "empty_dir", - "bad directory (dir #%lu) - no data block", - inode->i_ino); + if (err) + ext3_error(inode->i_sb, __FUNCTION__, + "error %d reading directory #%lu offset 0", + err, inode->i_ino); + else + ext3_warning(inode->i_sb, __FUNCTION__, + "bad directory (dir #%lu) - no data block", + inode->i_ino); return 1; } de = (struct ext3_dir_entry_2 *) bh->b_data; @@ -1792,24 +1821,26 @@ static int empty_dir (struct inode * inode) while (offset < inode->i_size ) { if (!bh || (void *) de >= (void *) (bh->b_data+sb->s_blocksize)) { + err = 0; brelse (bh); bh = ext3_bread (NULL, inode, offset >> EXT3_BLOCK_SIZE_BITS(sb), 0, &err); if (!bh) { -#if 0 - ext3_error (sb, "empty_dir", - "directory #%lu contains a hole at offset %lu", - inode->i_ino, offset); -#endif + if (err) + ext3_error(sb, __FUNCTION__, + "error %d reading directory" + " #%lu offset %lu", + err, inode->i_ino, offset); offset += sb->s_blocksize; continue; } de = (struct ext3_dir_entry_2 *) bh->b_data; } - if (!ext3_check_dir_entry ("empty_dir", inode, de, bh, - offset)) { - brelse (bh); - return 1; + if (!ext3_check_dir_entry("empty_dir", inode, de, bh, offset)) { + de = (struct ext3_dir_entry_2 *)(bh->b_data + + sb->s_blocksize); + offset = (offset | (sb->s_blocksize - 1)) + 1; + continue; } if (le32_to_cpu(de->inode)) { brelse (bh); @@ -1953,8 +1984,6 @@ int ext3_orphan_del(handle_t *handle, struct inode *inode) goto out_brelse; NEXT_ORPHAN(inode) = 0; err = ext3_mark_iloc_dirty(handle, inode, &iloc); - if (err) - goto out_brelse; out_err: ext3_std_error(inode->i_sb, err); @@ -1978,7 +2007,7 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry) /* Initialize quotas before so that eventual writes go in * separate transaction */ DQUOT_INIT(dentry->d_inode); - handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS); + handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2014,7 +2043,7 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry) * recovery. */ inode->i_size = 0; ext3_orphan_add(handle, inode); - inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; ext3_mark_inode_dirty(handle, inode); dir->i_nlink--; ext3_update_dx_flag(dir); @@ -2037,7 +2066,7 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry) /* Initialize quotas before so that eventual writes go * in separate transaction */ DQUOT_INIT(dentry->d_inode); - handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS); + handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2064,7 +2093,7 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry) retval = ext3_delete_entry(handle, dir, de, bh); if (retval) goto end_unlink; - dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; ext3_update_dx_flag(dir); ext3_mark_inode_dirty(handle, dir); inode->i_nlink--; @@ -2085,15 +2114,16 @@ static int ext3_symlink (struct inode * dir, { handle_t *handle; struct inode * inode; - int l, err; + int l, err, retries = 0; l = strlen(symname)+1; if (l > dir->i_sb->s_blocksize) return -ENAMETOOLONG; - handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + +retry: + handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5 + - 2*EXT3_QUOTA_INIT_BLOCKS); + 2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2113,7 +2143,8 @@ static int ext3_symlink (struct inode * dir, * We have a transaction open. All is sweetness. It also sets * i_size in generic_commit_write(). */ - err = page_symlink(inode, symname, l); + err = __page_symlink(inode, symname, l, + mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS); if (err) { ext3_dec_count(handle, inode); ext3_mark_inode_dirty(handle, inode); @@ -2129,6 +2160,8 @@ static int ext3_symlink (struct inode * dir, err = ext3_add_nondir(handle, dentry, inode); out_stop: ext3_journal_stop(handle); + if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries)) + goto retry; return err; } @@ -2137,12 +2170,13 @@ static int ext3_link (struct dentry * old_dentry, { handle_t *handle; struct inode *inode = old_dentry->d_inode; - int err; + int err, retries = 0; if (inode->i_nlink >= EXT3_LINK_MAX) return -EMLINK; - handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + +retry: + handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + EXT3_INDEX_EXTRA_TRANS_BLOCKS); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2150,12 +2184,14 @@ static int ext3_link (struct dentry * old_dentry, if (IS_DIRSYNC(dir)) handle->h_sync = 1; - inode->i_ctime = CURRENT_TIME; + inode->i_ctime = CURRENT_TIME_SEC; ext3_inc_count(handle, inode); atomic_inc(&inode->i_count); err = ext3_add_nondir(handle, dentry, inode); ext3_journal_stop(handle); + if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries)) + goto retry; return err; } @@ -2182,7 +2218,8 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry, * in separate transaction */ if (new_dentry->d_inode) DQUOT_INIT(new_dentry->d_inode); - handle = ext3_journal_start(old_dir, 2 * EXT3_DATA_TRANS_BLOCKS + + handle = ext3_journal_start(old_dir, 2 * + EXT3_DATA_TRANS_BLOCKS(old_dir->i_sb) + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 2); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2234,7 +2271,7 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry, } else { BUFFER_TRACE(new_bh, "get write access"); ext3_journal_get_write_access(handle, new_bh); - new_de->inode = le32_to_cpu(old_inode->i_ino); + new_de->inode = cpu_to_le32(old_inode->i_ino); if (EXT3_HAS_INCOMPAT_FEATURE(new_dir->i_sb, EXT3_FEATURE_INCOMPAT_FILETYPE)) new_de->file_type = old_de->file_type; @@ -2249,17 +2286,21 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry, * Like most other Unix systems, set the ctime for inodes on a * rename. */ - old_inode->i_ctime = CURRENT_TIME; + old_inode->i_ctime = CURRENT_TIME_SEC; ext3_mark_inode_dirty(handle, old_inode); /* * ok, that's it */ - retval = ext3_delete_entry(handle, old_dir, old_de, old_bh); - if (retval == -ENOENT) { - /* - * old_de could have moved out from under us. - */ + if (le32_to_cpu(old_de->inode) != old_inode->i_ino || + old_de->name_len != old_dentry->d_name.len || + strncmp(old_de->name, old_dentry->d_name.name, old_de->name_len) || + (retval = ext3_delete_entry(handle, old_dir, + old_de, old_bh)) == -ENOENT) { + /* old_de could have moved from under us during htree split, so + * make sure that we are deleting the right entry. We might + * also be pointing to a stale entry in the unused part of + * old_bh so just checking inum and the name isn't enough. */ struct buffer_head *old_bh2; struct ext3_dir_entry_2 *old_de2; @@ -2278,14 +2319,14 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry, if (new_inode) { new_inode->i_nlink--; - new_inode->i_ctime = CURRENT_TIME; + new_inode->i_ctime = CURRENT_TIME_SEC; } - old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; + old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC; ext3_update_dx_flag(old_dir); if (dir_bh) { BUFFER_TRACE(dir_bh, "get_write_access"); ext3_journal_get_write_access(handle, dir_bh); - PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino); + PARENT_INO(dir_bh->b_data) = cpu_to_le32(new_dir->i_ino); BUFFER_TRACE(dir_bh, "call ext3_journal_dirty_metadata"); ext3_journal_dirty_metadata(handle, dir_bh); old_dir->i_nlink--; @@ -2327,18 +2368,24 @@ struct inode_operations ext3_dir_inode_operations = { .mknod = ext3_mknod, .rename = ext3_rename, .setattr = ext3_setattr, - .setxattr = ext3_setxattr, - .getxattr = ext3_getxattr, +#ifdef CONFIG_EXT3_FS_XATTR + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = ext3_listxattr, - .removexattr = ext3_removexattr, + .removexattr = generic_removexattr, +#endif .permission = ext3_permission, + .sync_flags = ext3_sync_flags, }; struct inode_operations ext3_special_inode_operations = { .setattr = ext3_setattr, - .setxattr = ext3_setxattr, - .getxattr = ext3_getxattr, +#ifdef CONFIG_EXT3_FS_XATTR + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = ext3_listxattr, - .removexattr = ext3_removexattr, + .removexattr = generic_removexattr, +#endif .permission = ext3_permission, + .sync_flags = ext3_sync_flags, };