📄 dir.c
字号:
static int ocfs2_dir_foreach_blk_id(struct inode *inode, u64 *f_version, loff_t *f_pos, void *priv, filldir_t filldir, int *filldir_err){ 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. */ u64 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) { if (filldir_err) *filldir_err = 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, u64 *f_version, loff_t *f_pos, void *priv, filldir_t filldir, int *filldir_err){ int error = 0; unsigned long offset, blk, last_ra_blk = 0; int i, stored; struct buffer_head * bh, * tmp; struct ocfs2_dir_entry * de; int err; struct super_block * sb = inode->i_sb; unsigned int ra_sectors = 16; stored = 0; bh = NULL; offset = (*f_pos) & (sb->s_blocksize - 1); while (!error && !stored && *f_pos < i_size_read(inode)) { blk = (*f_pos) >> sb->s_blocksize_bits; bh = ocfs2_bread(inode, blk, &err, 0); if (!bh) { mlog(ML_ERROR, "directory #%llu contains a hole at offset %lld\n", (unsigned long long)OCFS2_I(inode)->ip_blkno, *f_pos); *f_pos += sb->s_blocksize - offset; continue; } /* The idea here is to begin with 8k read-ahead and to stay * 4k ahead of our current position. * * TODO: Use the pagecache for this. We just need to * make sure it's cluster-safe... */ if (!last_ra_blk || (((last_ra_blk - blk) << 9) <= (ra_sectors / 2))) { for (i = ra_sectors >> (sb->s_blocksize_bits - 9); i > 0; i--) { tmp = ocfs2_bread(inode, ++blk, &err, 1); if (tmp) brelse(tmp); } last_ra_blk = blk; ra_sectors = 8; }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 < sb->s_blocksize && i < offset; ) { de = (struct ocfs2_dir_entry *) (bh->b_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); } offset = i; *f_pos = ((*f_pos) & ~(sb->s_blocksize - 1)) | offset; *f_version = inode->i_version; } while (!error && *f_pos < i_size_read(inode) && offset < sb->s_blocksize) { de = (struct ocfs2_dir_entry *) (bh->b_data + offset); if (!ocfs2_check_dir_entry(inode, de, bh, offset)) { /* On error, skip the f_pos to the next block. */ *f_pos = ((*f_pos) | (sb->s_blocksize - 1)) + 1; brelse(bh); 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]; error = filldir(priv, de->name, de->name_len, *f_pos, le64_to_cpu(de->inode), d_type); if (error) { if (filldir_err) *filldir_err = error; break; } if (version != *f_version) goto revalidate; stored ++; } *f_pos += le16_to_cpu(de->rec_len); } offset = 0; brelse(bh); } stored = 0;out: return stored;}static int ocfs2_dir_foreach_blk(struct inode *inode, u64 *f_version, loff_t *f_pos, void *priv, filldir_t filldir, int *filldir_err){ if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) return ocfs2_dir_foreach_blk_id(inode, f_version, f_pos, priv, filldir, filldir_err); return ocfs2_dir_foreach_blk_el(inode, f_version, f_pos, priv, filldir, filldir_err);}/* * This is intended to be called from inside other kernel functions, * so we fake some arguments. */int ocfs2_dir_foreach(struct inode *inode, loff_t *f_pos, void *priv, filldir_t filldir){ int ret = 0, filldir_err = 0; u64 version = inode->i_version; while (*f_pos < i_size_read(inode)) { ret = ocfs2_dir_foreach_blk(inode, &version, f_pos, priv, filldir, &filldir_err); if (ret || filldir_err) break; } if (ret > 0) ret = -EIO; return 0;}/* * ocfs2_readdir() * */int ocfs2_readdir(struct file * filp, void * dirent, filldir_t filldir){ int error = 0; struct inode *inode = filp->f_path.dentry->d_inode; int lock_level = 0; mlog_entry("dirino=%llu\n", (unsigned long long)OCFS2_I(inode)->ip_blkno); error = ocfs2_meta_lock_atime(inode, filp->f_vfsmnt, &lock_level); if (lock_level && error >= 0) { /* We release EX lock which used to update atime * and get PR lock again to reduce contention * on commonly accessed directories. */ ocfs2_meta_unlock(inode, 1); lock_level = 0; error = ocfs2_meta_lock(inode, NULL, 0); } if (error < 0) { if (error != -ENOENT) mlog_errno(error); /* we haven't got any yet, so propagate the error. */ goto bail_nolock; } error = ocfs2_dir_foreach_blk(inode, &filp->f_version, &filp->f_pos, dirent, filldir, NULL); ocfs2_meta_unlock(inode, lock_level);bail_nolock: mlog_exit(error); return error;}/* * NOTE: this should always be called with parent dir i_mutex taken. */int ocfs2_find_files_on_disk(const char *name, int namelen, u64 *blkno, struct inode *inode, struct buffer_head **dirent_bh, struct ocfs2_dir_entry **dirent){ int status = -ENOENT; mlog_entry("(name=%.*s, blkno=%p, inode=%p, dirent_bh=%p, dirent=%p)\n", namelen, name, blkno, inode, dirent_bh, dirent); *dirent_bh = ocfs2_find_entry(name, namelen, inode, dirent); if (!*dirent_bh || !*dirent) { status = -ENOENT; goto leave; } *blkno = le64_to_cpu((*dirent)->inode); status = 0;leave: if (status < 0) { *dirent = NULL; if (*dirent_bh) { brelse(*dirent_bh); *dirent_bh = NULL; } } mlog_exit(status); return status;}/* * Convenience function for callers which just want the block number * mapped to a name and don't require the full dirent info, etc. */int ocfs2_lookup_ino_from_name(struct inode *dir, const char *name, int namelen, u64 *blkno){ int ret; struct buffer_head *bh = NULL; struct ocfs2_dir_entry *dirent = NULL; ret = ocfs2_find_files_on_disk(name, namelen, blkno, dir, &bh, &dirent); brelse(bh); return ret;}/* Check for a name within a directory. * * Return 0 if the name does not exist * Return -EEXIST if the directory contains the name * * Callers should have i_mutex + a cluster lock on dir */int ocfs2_check_dir_for_entry(struct inode *dir, const char *name, int namelen){ int ret; struct buffer_head *dirent_bh = NULL; struct ocfs2_dir_entry *dirent = NULL; mlog_entry("dir %llu, name '%.*s'\n", (unsigned long long)OCFS2_I(dir)->ip_blkno, namelen, name); ret = -EEXIST; dirent_bh = ocfs2_find_entry(name, namelen, dir, &dirent); if (dirent_bh) goto bail; ret = 0;bail: if (dirent_bh) brelse(dirent_bh); mlog_exit(ret); return ret;}struct ocfs2_empty_dir_priv { unsigned seen_dot; unsigned seen_dot_dot; unsigned seen_other;};static int ocfs2_empty_dir_filldir(void *priv, const char *name, int name_len, loff_t pos, u64 ino, unsigned type){ struct ocfs2_empty_dir_priv *p = priv; /* * Check the positions of "." and ".." records to be sure * they're in the correct place. */ if (name_len == 1 && !strncmp(".", name, 1) && pos == 0) { p->seen_dot = 1; return 0; } if (name_len == 2 && !strncmp("..", name, 2) && pos == OCFS2_DIR_REC_LEN(1)) { p->seen_dot_dot = 1; return 0; } p->seen_other = 1; return 1;}/* * routine to check that the specified directory is empty (for rmdir) * * Returns 1 if dir is empty, zero otherwise. */int ocfs2_empty_dir(struct inode *inode){ int ret; loff_t start = 0; struct ocfs2_empty_dir_priv priv; memset(&priv, 0, sizeof(priv)); ret = ocfs2_dir_foreach(inode, &start, &priv, ocfs2_empty_dir_filldir); if (ret) mlog_errno(ret); if (!priv.seen_dot || !priv.seen_dot_dot) { mlog(ML_ERROR, "bad directory (dir #%llu) - no `.' or `..'\n", (unsigned long long)OCFS2_I(inode)->ip_blkno); /* * XXX: Is it really safe to allow an unlink to continue? */ return 1; } return !priv.seen_other;}static void ocfs2_fill_initial_dirents(struct inode *inode, struct inode *parent, char *start, unsigned int size){ struct ocfs2_dir_entry *de = (struct ocfs2_dir_entry *)start; de->inode = cpu_to_le64(OCFS2_I(inode)->ip_blkno); de->name_len = 1; de->rec_len = cpu_to_le16(OCFS2_DIR_REC_LEN(de->name_len)); strcpy(de->name, "."); ocfs2_set_de_type(de, S_IFDIR); de = (struct ocfs2_dir_entry *) ((char *)de + le16_to_cpu(de->rec_len)); de->inode = cpu_to_le64(OCFS2_I(parent)->ip_blkno); de->rec_len = cpu_to_le16(size - OCFS2_DIR_REC_LEN(1)); de->name_len = 2; strcpy(de->name, ".."); ocfs2_set_de_type(de, S_IFDIR);}/* * This works together with code in ocfs2_mknod_locked() which sets * the inline-data flag and initializes the inline-data section. */static int ocfs2_fill_new_dir_id(struct ocfs2_super *osb, handle_t *handle, struct inode *parent, struct inode *inode, struct buffer_head *di_bh){ int ret; struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; struct ocfs2_inline_data *data = &di->id2.i_data; unsigned int size = le16_to_cpu(data->id_count); ret = ocfs2_journal_access(handle, inode, di_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (ret) { mlog_errno(ret); goto out; } ocfs2_fill_initial_dirents(inode, parent, data->id_data, size); ocfs2_journal_dirty(handle, di_bh); if (ret) { mlog_errno(ret); goto out; } i_size_write(inode, size); inode->i_nlink = 2; inode->i_blocks = ocfs2_inode_sector_count(inode); ret = ocfs2_mark_inode_dirty(handle, inode, di_bh); if (ret < 0) mlog_errno(ret);out: return ret;}static int ocfs2_fill_new_dir_el(struct ocfs2_super *osb, handle_t *handle, struct inode *parent, struct inode *inode, struct buffer_head *fe_bh, struct ocfs2_alloc_context *data_ac){ int status; struct buffer_head *new_bh = NULL; mlog_entry_void(); status = ocfs2_do_extend_dir(osb->sb, handle, inode, fe_bh, data_ac, NULL, &new_bh); if (status < 0) { mlog_errno(status); goto bail; } ocfs2_set_new_buffer_uptodate(inode, new_bh); status = ocfs2_journal_access(handle, inode, new_bh, OCFS2_JOURNAL_ACCESS_CREATE); if (status < 0) { mlog_errno(status); goto bail; } memset(new_bh->b_data, 0, osb->sb->s_blocksize); ocfs2_fill_initial_dirents(inode, parent, new_bh->b_data, osb->sb->s_blocksize); status = ocfs2_journal_dirty(handle, new_bh); if (status < 0) { mlog_errno(status); goto bail; } i_size_write(inode, inode->i_sb->s_blocksize); inode->i_nlink = 2; inode->i_blocks = ocfs2_inode_sector_count(inode); status = ocfs2_mark_inode_dirty(handle, inode, fe_bh); if (status < 0) { mlog_errno(status); goto bail; } status = 0;bail: if (new_bh) brelse(new_bh); mlog_exit(status); return status;}int ocfs2_fill_new_dir(struct ocfs2_super *osb, handle_t *handle, struct inode *parent, struct inode *inode, struct buffer_head *fe_bh, struct ocfs2_alloc_context *data_ac){ BUG_ON(!ocfs2_supports_inline_data(osb) && data_ac == NULL); if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) return ocfs2_fill_new_dir_id(osb, handle, parent, inode, fe_bh); return ocfs2_fill_new_dir_el(osb, handle, parent, inode, fe_bh, data_ac);}static void ocfs2_expand_last_dirent(char *start, unsigned int old_size, unsigned int new_size){ struct ocfs2_dir_entry *de; struct ocfs2_dir_entry *prev_de; char *de_buf, *limit; unsigned int bytes = new_size - old_size; limit = start + old_size; de_buf = start; de = (struct ocfs2_dir_entry *)de_buf; do { prev_de = de; de_buf += le16_to_cpu(de->rec_len); de = (struct ocfs2_dir_entry *)de_buf; } while (de_buf < limit); le16_add_cpu(&prev_de->rec_len, bytes);}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -