|
@@ -81,6 +81,10 @@ static int ocfs2_do_extend_dir(struct super_block *sb,
|
|
struct ocfs2_alloc_context *meta_ac,
|
|
struct ocfs2_alloc_context *meta_ac,
|
|
struct buffer_head **new_bh);
|
|
struct buffer_head **new_bh);
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * bh passed here can be an inode block or a dir data block, depending
|
|
|
|
+ * on the inode inline data flag.
|
|
|
|
+ */
|
|
static int ocfs2_check_dir_entry(struct inode * dir,
|
|
static int ocfs2_check_dir_entry(struct inode * dir,
|
|
struct ocfs2_dir_entry * de,
|
|
struct ocfs2_dir_entry * de,
|
|
struct buffer_head * bh,
|
|
struct buffer_head * bh,
|
|
@@ -125,6 +129,8 @@ static int inline ocfs2_search_dirblock(struct buffer_head *bh,
|
|
struct inode *dir,
|
|
struct inode *dir,
|
|
const char *name, int namelen,
|
|
const char *name, int namelen,
|
|
unsigned long offset,
|
|
unsigned long offset,
|
|
|
|
+ char *first_de,
|
|
|
|
+ unsigned int bytes,
|
|
struct ocfs2_dir_entry **res_dir)
|
|
struct ocfs2_dir_entry **res_dir)
|
|
{
|
|
{
|
|
struct ocfs2_dir_entry *de;
|
|
struct ocfs2_dir_entry *de;
|
|
@@ -134,8 +140,8 @@ static int inline ocfs2_search_dirblock(struct buffer_head *bh,
|
|
|
|
|
|
mlog_entry_void();
|
|
mlog_entry_void();
|
|
|
|
|
|
- de_buf = bh->b_data;
|
|
|
|
- dlimit = de_buf + dir->i_sb->s_blocksize;
|
|
|
|
|
|
+ de_buf = first_de;
|
|
|
|
+ dlimit = de_buf + bytes;
|
|
|
|
|
|
while (de_buf < dlimit) {
|
|
while (de_buf < dlimit) {
|
|
/* this code is executed quadratically often */
|
|
/* this code is executed quadratically often */
|
|
@@ -171,9 +177,39 @@ bail:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-struct buffer_head *ocfs2_find_entry(const char *name, int namelen,
|
|
|
|
- struct inode *dir,
|
|
|
|
- struct ocfs2_dir_entry **res_dir)
|
|
|
|
|
|
+static struct buffer_head *ocfs2_find_entry_id(const char *name,
|
|
|
|
+ int namelen,
|
|
|
|
+ struct inode *dir,
|
|
|
|
+ struct ocfs2_dir_entry **res_dir)
|
|
|
|
+{
|
|
|
|
+ int ret, found;
|
|
|
|
+ struct buffer_head *di_bh = NULL;
|
|
|
|
+ struct ocfs2_dinode *di;
|
|
|
|
+ struct ocfs2_inline_data *data;
|
|
|
|
+
|
|
|
|
+ ret = ocfs2_read_block(OCFS2_SB(dir->i_sb), OCFS2_I(dir)->ip_blkno,
|
|
|
|
+ &di_bh, OCFS2_BH_CACHED, dir);
|
|
|
|
+ if (ret) {
|
|
|
|
+ mlog_errno(ret);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ di = (struct ocfs2_dinode *)di_bh->b_data;
|
|
|
|
+ data = &di->id2.i_data;
|
|
|
|
+
|
|
|
|
+ found = ocfs2_search_dirblock(di_bh, dir, name, namelen, 0,
|
|
|
|
+ data->id_data, i_size_read(dir), res_dir);
|
|
|
|
+ if (found == 1)
|
|
|
|
+ return di_bh;
|
|
|
|
+
|
|
|
|
+ brelse(di_bh);
|
|
|
|
+out:
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+struct buffer_head *ocfs2_find_entry_el(const char *name, int namelen,
|
|
|
|
+ struct inode *dir,
|
|
|
|
+ struct ocfs2_dir_entry **res_dir)
|
|
{
|
|
{
|
|
struct super_block *sb;
|
|
struct super_block *sb;
|
|
struct buffer_head *bh_use[NAMEI_RA_SIZE];
|
|
struct buffer_head *bh_use[NAMEI_RA_SIZE];
|
|
@@ -188,7 +224,6 @@ struct buffer_head *ocfs2_find_entry(const char *name, int namelen,
|
|
|
|
|
|
mlog_entry_void();
|
|
mlog_entry_void();
|
|
|
|
|
|
- *res_dir = NULL;
|
|
|
|
sb = dir->i_sb;
|
|
sb = dir->i_sb;
|
|
|
|
|
|
nblocks = i_size_read(dir) >> sb->s_blocksize_bits;
|
|
nblocks = i_size_read(dir) >> sb->s_blocksize_bits;
|
|
@@ -236,6 +271,7 @@ restart:
|
|
}
|
|
}
|
|
i = ocfs2_search_dirblock(bh, dir, name, namelen,
|
|
i = ocfs2_search_dirblock(bh, dir, name, namelen,
|
|
block << sb->s_blocksize_bits,
|
|
block << sb->s_blocksize_bits,
|
|
|
|
+ bh->b_data, sb->s_blocksize,
|
|
res_dir);
|
|
res_dir);
|
|
if (i == 1) {
|
|
if (i == 1) {
|
|
OCFS2_I(dir)->ip_dir_start_lookup = block;
|
|
OCFS2_I(dir)->ip_dir_start_lookup = block;
|
|
@@ -271,6 +307,30 @@ cleanup_and_exit:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Try to find an entry of the provided name within 'dir'.
|
|
|
|
+ *
|
|
|
|
+ * If nothing was found, NULL is returned. Otherwise, a buffer_head
|
|
|
|
+ * and pointer to the dir entry are passed back.
|
|
|
|
+ *
|
|
|
|
+ * Caller can NOT assume anything about the contents of the
|
|
|
|
+ * buffer_head - it is passed back only so that it can be passed into
|
|
|
|
+ * any one of the manipulation functions (add entry, delete entry,
|
|
|
|
+ * etc). As an example, bh in the extent directory case is a data
|
|
|
|
+ * block, in the inline-data case it actually points to an inode.
|
|
|
|
+ */
|
|
|
|
+struct buffer_head *ocfs2_find_entry(const char *name, int namelen,
|
|
|
|
+ struct inode *dir,
|
|
|
|
+ struct ocfs2_dir_entry **res_dir)
|
|
|
|
+{
|
|
|
|
+ *res_dir = NULL;
|
|
|
|
+
|
|
|
|
+ if (OCFS2_I(dir)->ip_dyn_features & OCFS2_INLINE_DATA_FL)
|
|
|
|
+ return ocfs2_find_entry_id(name, namelen, dir, res_dir);
|
|
|
|
+
|
|
|
|
+ return ocfs2_find_entry_el(name, namelen, dir, res_dir);
|
|
|
|
+}
|
|
|
|
+
|
|
int ocfs2_update_entry(struct inode *dir, handle_t *handle,
|
|
int ocfs2_update_entry(struct inode *dir, handle_t *handle,
|
|
struct buffer_head *de_bh, struct ocfs2_dir_entry *de,
|
|
struct buffer_head *de_bh, struct ocfs2_dir_entry *de,
|
|
struct inode *new_entry_inode)
|
|
struct inode *new_entry_inode)
|
|
@@ -459,8 +519,98 @@ bail:
|
|
return retval;
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
|
|
-static int ocfs2_dir_foreach_blk(struct inode *inode, unsigned long *f_version,
|
|
|
|
- loff_t *f_pos, void *priv, filldir_t filldir)
|
|
|
|
|
|
+static int ocfs2_dir_foreach_blk_id(struct inode *inode,
|
|
|
|
+ unsigned long *f_version,
|
|
|
|
+ loff_t *f_pos, void *priv,
|
|
|
|
+ filldir_t filldir)
|
|
|
|
+{
|
|
|
|
+ int ret, i, filldir_ret;
|
|
|
|
+ unsigned long offset = *f_pos;
|
|
|
|
+ struct buffer_head *di_bh = NULL;
|
|
|
|
+ struct ocfs2_dinode *di;
|
|
|
|
+ struct ocfs2_inline_data *data;
|
|
|
|
+ struct ocfs2_dir_entry *de;
|
|
|
|
+
|
|
|
|
+ ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno,
|
|
|
|
+ &di_bh, OCFS2_BH_CACHED, inode);
|
|
|
|
+ if (ret) {
|
|
|
|
+ mlog(ML_ERROR, "Unable to read inode block for dir %llu\n",
|
|
|
|
+ (unsigned long long)OCFS2_I(inode)->ip_blkno);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ di = (struct ocfs2_dinode *)di_bh->b_data;
|
|
|
|
+ data = &di->id2.i_data;
|
|
|
|
+
|
|
|
|
+ while (*f_pos < i_size_read(inode)) {
|
|
|
|
+revalidate:
|
|
|
|
+ /* If the dir block has changed since the last call to
|
|
|
|
+ * readdir(2), then we might be pointing to an invalid
|
|
|
|
+ * dirent right now. Scan from the start of the block
|
|
|
|
+ * to make sure. */
|
|
|
|
+ if (*f_version != inode->i_version) {
|
|
|
|
+ for (i = 0; i < i_size_read(inode) && i < offset; ) {
|
|
|
|
+ de = (struct ocfs2_dir_entry *)
|
|
|
|
+ (data->id_data + i);
|
|
|
|
+ /* It's too expensive to do a full
|
|
|
|
+ * dirent test each time round this
|
|
|
|
+ * loop, but we do have to test at
|
|
|
|
+ * least that it is non-zero. A
|
|
|
|
+ * failure will be detected in the
|
|
|
|
+ * dirent test below. */
|
|
|
|
+ if (le16_to_cpu(de->rec_len) <
|
|
|
|
+ OCFS2_DIR_REC_LEN(1))
|
|
|
|
+ break;
|
|
|
|
+ i += le16_to_cpu(de->rec_len);
|
|
|
|
+ }
|
|
|
|
+ *f_pos = offset = i;
|
|
|
|
+ *f_version = inode->i_version;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ de = (struct ocfs2_dir_entry *) (data->id_data + *f_pos);
|
|
|
|
+ if (!ocfs2_check_dir_entry(inode, de, di_bh, *f_pos)) {
|
|
|
|
+ /* On error, skip the f_pos to the end. */
|
|
|
|
+ *f_pos = i_size_read(inode);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ offset += le16_to_cpu(de->rec_len);
|
|
|
|
+ if (le64_to_cpu(de->inode)) {
|
|
|
|
+ /* We might block in the next section
|
|
|
|
+ * if the data destination is
|
|
|
|
+ * currently swapped out. So, use a
|
|
|
|
+ * version stamp to detect whether or
|
|
|
|
+ * not the directory has been modified
|
|
|
|
+ * during the copy operation.
|
|
|
|
+ */
|
|
|
|
+ unsigned long version = *f_version;
|
|
|
|
+ unsigned char d_type = DT_UNKNOWN;
|
|
|
|
+
|
|
|
|
+ if (de->file_type < OCFS2_FT_MAX)
|
|
|
|
+ d_type = ocfs2_filetype_table[de->file_type];
|
|
|
|
+
|
|
|
|
+ filldir_ret = filldir(priv, de->name,
|
|
|
|
+ de->name_len,
|
|
|
|
+ *f_pos,
|
|
|
|
+ le64_to_cpu(de->inode),
|
|
|
|
+ d_type);
|
|
|
|
+ if (filldir_ret)
|
|
|
|
+ break;
|
|
|
|
+ if (version != *f_version)
|
|
|
|
+ goto revalidate;
|
|
|
|
+ }
|
|
|
|
+ *f_pos += le16_to_cpu(de->rec_len);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ brelse(di_bh);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int ocfs2_dir_foreach_blk_el(struct inode *inode,
|
|
|
|
+ unsigned long *f_version,
|
|
|
|
+ loff_t *f_pos, void *priv,
|
|
|
|
+ filldir_t filldir)
|
|
{
|
|
{
|
|
int error = 0;
|
|
int error = 0;
|
|
unsigned long offset, blk, last_ra_blk = 0;
|
|
unsigned long offset, blk, last_ra_blk = 0;
|
|
@@ -576,6 +726,16 @@ out:
|
|
return stored;
|
|
return stored;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int ocfs2_dir_foreach_blk(struct inode *inode, unsigned long *f_version,
|
|
|
|
+ loff_t *f_pos, void *priv, filldir_t filldir)
|
|
|
|
+{
|
|
|
|
+ if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL)
|
|
|
|
+ return ocfs2_dir_foreach_blk_id(inode, f_version, f_pos, priv,
|
|
|
|
+ filldir);
|
|
|
|
+
|
|
|
|
+ return ocfs2_dir_foreach_blk_el(inode, f_version, f_pos, priv, filldir);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* This is intended to be called from inside other kernel functions,
|
|
* This is intended to be called from inside other kernel functions,
|
|
* so we fake some arguments.
|
|
* so we fake some arguments.
|