📄 dir.c
字号:
/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * dir.c * * Creates, reads, walks and deletes directory-nodes * * Copyright (C) 2002, 2004 Oracle. All rights reserved. * * Portions of this code from linux/fs/ext3/dir.c * * Copyright (C) 1992, 1993, 1994, 1995 * Remy Card (card@masi.ibp.fr) * Laboratoire MASI - Institut Blaise pascal * Universite Pierre et Marie Curie (Paris VI) * * from * * linux/fs/minix/dir.c * * Copyright (C) 1991, 1992 Linux Torvalds * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. */#include <linux/fs.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/highmem.h>#define MLOG_MASK_PREFIX ML_NAMEI#include <cluster/masklog.h>#include "ocfs2.h"#include "alloc.h"#include "dir.h"#include "dlmglue.h"#include "extent_map.h"#include "file.h"#include "inode.h"#include "journal.h"#include "namei.h"#include "suballoc.h"#include "uptodate.h"#include "buffer_head_io.h"static unsigned char ocfs2_filetype_table[] = { DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK};static int ocfs2_extend_dir(struct ocfs2_super *osb, struct inode *dir, struct buffer_head *parent_fe_bh, struct buffer_head **new_de_bh);/* * ocfs2_readdir() * */int ocfs2_readdir(struct file * filp, void * dirent, filldir_t filldir){ int error = 0; unsigned long offset, blk; int i, num, stored; struct buffer_head * bh, * tmp; struct ocfs2_dir_entry * de; int err; struct inode *inode = filp->f_dentry->d_inode; struct super_block * sb = inode->i_sb; int have_disk_lock = 0; mlog_entry("dirino=%"MLFu64"\n", OCFS2_I(inode)->ip_blkno); stored = 0; bh = NULL; error = ocfs2_meta_lock(inode, NULL, NULL, 0); if (error < 0) { if (error != -ENOENT) mlog_errno(error); /* we haven't got any yet, so propagate the error. */ stored = error; goto bail; } have_disk_lock = 1; offset = filp->f_pos & (sb->s_blocksize - 1); while (!error && !stored && filp->f_pos < i_size_read(inode)) { blk = (filp->f_pos) >> sb->s_blocksize_bits; bh = ocfs2_bread(inode, blk, &err, 0); if (!bh) { mlog(ML_ERROR, "directory #%"MLFu64" contains a hole " "at offset %lld\n", OCFS2_I(inode)->ip_blkno, filp->f_pos); filp->f_pos += sb->s_blocksize - offset; continue; } /* * Do the readahead (8k) */ if (!offset) { for (i = 16 >> (sb->s_blocksize_bits - 9), num = 0; i > 0; i--) { tmp = ocfs2_bread(inode, ++blk, &err, 1); if (tmp) brelse(tmp); } }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 (filp->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; filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) | offset; filp->f_version = inode->i_version; } while (!error && filp->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. */ filp->f_pos = (filp->f_pos | (sb->s_blocksize - 1)) + 1; brelse(bh); goto bail; } 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 = filp->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(dirent, de->name, de->name_len, filp->f_pos, ino_from_blkno(sb, le64_to_cpu(de->inode)), d_type); if (error) break; if (version != filp->f_version) goto revalidate; stored ++; } filp->f_pos += le16_to_cpu(de->rec_len); } offset = 0; brelse(bh); } stored = 0;bail: if (have_disk_lock) ocfs2_meta_unlock(inode, 0); mlog_exit(stored); return stored;}/* * 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; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); mlog_entry("(osb=%p, parent=%"MLFu64", name='%.*s', blkno=%p, " "inode=%p)\n", osb, OCFS2_I(inode)->ip_blkno, namelen, name, blkno, inode); *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;}/* 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 %"MLFu64", name '%.*s'\n", 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;}/* * routine to check that the specified directory is empty (for rmdir) */int ocfs2_empty_dir(struct inode *inode){ unsigned long offset; struct buffer_head * bh; struct ocfs2_dir_entry * de, * de1; struct super_block * sb; int err; sb = inode->i_sb; if ((i_size_read(inode) < (OCFS2_DIR_REC_LEN(1) + OCFS2_DIR_REC_LEN(2))) || !(bh = ocfs2_bread(inode, 0, &err, 0))) { mlog(ML_ERROR, "bad directory (dir #%"MLFu64") - " "no data block\n", OCFS2_I(inode)->ip_blkno); return 1; } de = (struct ocfs2_dir_entry *) bh->b_data; de1 = (struct ocfs2_dir_entry *) ((char *)de + le16_to_cpu(de->rec_len)); if ((le64_to_cpu(de->inode) != OCFS2_I(inode)->ip_blkno) || !le64_to_cpu(de1->inode) || strcmp(".", de->name) || strcmp("..", de1->name)) { mlog(ML_ERROR, "bad directory (dir #%"MLFu64") - " "no `.' or `..'\n", OCFS2_I(inode)->ip_blkno); brelse(bh); return 1; } offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len); de = (struct ocfs2_dir_entry *)((char *)de1 + le16_to_cpu(de1->rec_len)); while (offset < i_size_read(inode) ) { if (!bh || (void *)de >= (void *)(bh->b_data + sb->s_blocksize)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -