⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dir.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* -*- 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 "super.h"#include "uptodate.h"#include "buffer_head_io.h"#define NAMEI_RA_CHUNKS  2#define NAMEI_RA_BLOCKS  4#define NAMEI_RA_SIZE        (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS)#define NAMEI_RA_INDEX(c,b)  (((c) * NAMEI_RA_BLOCKS) + (b))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,			    unsigned int blocks_wanted,			    struct buffer_head **new_de_bh);static int ocfs2_do_extend_dir(struct super_block *sb,			       handle_t *handle,			       struct inode *dir,			       struct buffer_head *parent_fe_bh,			       struct ocfs2_alloc_context *data_ac,			       struct ocfs2_alloc_context *meta_ac,			       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,				 struct ocfs2_dir_entry * de,				 struct buffer_head * bh,				 unsigned long offset){	const char *error_msg = NULL;	const int rlen = le16_to_cpu(de->rec_len);	if (rlen < OCFS2_DIR_REC_LEN(1))		error_msg = "rec_len is smaller than minimal";	else if (rlen % 4 != 0)		error_msg = "rec_len % 4 != 0";	else if (rlen < OCFS2_DIR_REC_LEN(de->name_len))		error_msg = "rec_len is too small for name_len";	else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize)		error_msg = "directory entry across blocks";	if (error_msg != NULL)		mlog(ML_ERROR, "bad entry in directory #%llu: %s - "		     "offset=%lu, inode=%llu, rec_len=%d, name_len=%d\n",		     (unsigned long long)OCFS2_I(dir)->ip_blkno, error_msg,		     offset, (unsigned long long)le64_to_cpu(de->inode), rlen,		     de->name_len);	return error_msg == NULL ? 1 : 0;}static inline int ocfs2_match(int len,			      const char * const name,			      struct ocfs2_dir_entry *de){	if (len != de->name_len)		return 0;	if (!de->inode)		return 0;	return !memcmp(name, de->name, len);}/* * Returns 0 if not found, -1 on failure, and 1 on success */static int inline ocfs2_search_dirblock(struct buffer_head *bh,					struct inode *dir,					const char *name, int namelen,					unsigned long offset,					char *first_de,					unsigned int bytes,					struct ocfs2_dir_entry **res_dir){	struct ocfs2_dir_entry *de;	char *dlimit, *de_buf;	int de_len;	int ret = 0;	mlog_entry_void();	de_buf = first_de;	dlimit = de_buf + bytes;	while (de_buf < dlimit) {		/* this code is executed quadratically often */		/* do minimal checking `by hand' */		de = (struct ocfs2_dir_entry *) de_buf;		if (de_buf + namelen <= dlimit &&		    ocfs2_match(namelen, name, de)) {			/* found a match - just to be sure, do a full check */			if (!ocfs2_check_dir_entry(dir, de, bh, offset)) {				ret = -1;				goto bail;			}			*res_dir = de;			ret = 1;			goto bail;		}		/* prevent looping on a bad block */		de_len = le16_to_cpu(de->rec_len);		if (de_len <= 0) {			ret = -1;			goto bail;		}		de_buf += de_len;		offset += de_len;	}bail:	mlog_exit(ret);	return ret;}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;}static 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 buffer_head *bh_use[NAMEI_RA_SIZE];	struct buffer_head *bh, *ret = NULL;	unsigned long start, block, b;	int ra_max = 0;		/* Number of bh's in the readahead				   buffer, bh_use[] */	int ra_ptr = 0;		/* Current index into readahead				   buffer */	int num = 0;	int nblocks, i, err;	mlog_entry_void();	sb = dir->i_sb;	nblocks = i_size_read(dir) >> sb->s_blocksize_bits;	start = OCFS2_I(dir)->ip_dir_start_lookup;	if (start >= nblocks)		start = 0;	block = start;restart:	do {		/*		 * We deal with the read-ahead logic here.		 */		if (ra_ptr >= ra_max) {			/* Refill the readahead buffer */			ra_ptr = 0;			b = block;			for (ra_max = 0; ra_max < NAMEI_RA_SIZE; ra_max++) {				/*				 * Terminate if we reach the end of the				 * directory and must wrap, or if our				 * search has finished at this block.				 */				if (b >= nblocks || (num && block == start)) {					bh_use[ra_max] = NULL;					break;				}				num++;				bh = ocfs2_bread(dir, b++, &err, 1);				bh_use[ra_max] = bh;			}		}		if ((bh = bh_use[ra_ptr++]) == NULL)			goto next;		wait_on_buffer(bh);		if (!buffer_uptodate(bh)) {			/* read error, skip block & hope for the best */			ocfs2_error(dir->i_sb, "reading directory %llu, "				    "offset %lu\n",				    (unsigned long long)OCFS2_I(dir)->ip_blkno,				    block);			brelse(bh);			goto next;		}		i = ocfs2_search_dirblock(bh, dir, name, namelen,					  block << sb->s_blocksize_bits,					  bh->b_data, sb->s_blocksize,					  res_dir);		if (i == 1) {			OCFS2_I(dir)->ip_dir_start_lookup = block;			ret = bh;			goto cleanup_and_exit;		} else {			brelse(bh);			if (i < 0)				goto cleanup_and_exit;		}	next:		if (++block >= nblocks)			block = 0;	} while (block != start);	/*	 * If the directory has grown while we were searching, then	 * search the last part of the directory before giving up.	 */	block = nblocks;	nblocks = i_size_read(dir) >> sb->s_blocksize_bits;	if (block < nblocks) {		start = 0;		goto restart;	}cleanup_and_exit:	/* Clean up the read-ahead blocks */	for (; ra_ptr < ra_max; ra_ptr++)		brelse(bh_use[ra_ptr]);	mlog_exit_ptr(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);}/* * Update inode number and type of a previously found directory entry. */int ocfs2_update_entry(struct inode *dir, handle_t *handle,		       struct buffer_head *de_bh, struct ocfs2_dir_entry *de,		       struct inode *new_entry_inode){	int ret;	/*	 * The same code works fine for both inline-data and extent	 * based directories, so no need to split this up.	 */	ret = ocfs2_journal_access(handle, dir, de_bh,				   OCFS2_JOURNAL_ACCESS_WRITE);	if (ret) {		mlog_errno(ret);		goto out;	}	de->inode = cpu_to_le64(OCFS2_I(new_entry_inode)->ip_blkno);	ocfs2_set_de_type(de, new_entry_inode->i_mode);	ocfs2_journal_dirty(handle, de_bh);out:	return ret;}static int __ocfs2_delete_entry(handle_t *handle, struct inode *dir,				struct ocfs2_dir_entry *de_del,				struct buffer_head *bh, char *first_de,				unsigned int bytes){	struct ocfs2_dir_entry *de, *pde;	int i, status = -ENOENT;	mlog_entry("(0x%p, 0x%p, 0x%p, 0x%p)\n", handle, dir, de_del, bh);	i = 0;	pde = NULL;	de = (struct ocfs2_dir_entry *) first_de;	while (i < bytes) {		if (!ocfs2_check_dir_entry(dir, de, bh, i)) {			status = -EIO;			mlog_errno(status);			goto bail;		}		if (de == de_del)  {			status = ocfs2_journal_access(handle, dir, bh,						      OCFS2_JOURNAL_ACCESS_WRITE);			if (status < 0) {				status = -EIO;				mlog_errno(status);				goto bail;			}			if (pde)				pde->rec_len =					cpu_to_le16(le16_to_cpu(pde->rec_len) +						    le16_to_cpu(de->rec_len));			else				de->inode = 0;			dir->i_version++;			status = ocfs2_journal_dirty(handle, bh);			goto bail;		}		i += le16_to_cpu(de->rec_len);		pde = de;		de = (struct ocfs2_dir_entry *)((char *)de + le16_to_cpu(de->rec_len));	}bail:	mlog_exit(status);	return status;}static inline int ocfs2_delete_entry_id(handle_t *handle,					struct inode *dir,					struct ocfs2_dir_entry *de_del,					struct buffer_head *bh){	int ret;	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;	ret = __ocfs2_delete_entry(handle, dir, de_del, bh, data->id_data,				   i_size_read(dir));	brelse(di_bh);out:	return ret;}static inline int ocfs2_delete_entry_el(handle_t *handle,					struct inode *dir,					struct ocfs2_dir_entry *de_del,					struct buffer_head *bh){	return __ocfs2_delete_entry(handle, dir, de_del, bh, bh->b_data,				    bh->b_size);}/* * ocfs2_delete_entry deletes a directory entry by merging it with the * previous entry */int ocfs2_delete_entry(handle_t *handle,		       struct inode *dir,		       struct ocfs2_dir_entry *de_del,		       struct buffer_head *bh){	if (OCFS2_I(dir)->ip_dyn_features & OCFS2_INLINE_DATA_FL)		return ocfs2_delete_entry_id(handle, dir, de_del, bh);	return ocfs2_delete_entry_el(handle, dir, de_del, bh);}/* * Check whether 'de' has enough room to hold an entry of * 'new_rec_len' bytes. */static inline int ocfs2_dirent_would_fit(struct ocfs2_dir_entry *de,					 unsigned int new_rec_len){	unsigned int de_really_used;	/* Check whether this is an empty record with enough space */	if (le64_to_cpu(de->inode) == 0 &&	    le16_to_cpu(de->rec_len) >= new_rec_len)		return 1;	/*	 * Record might have free space at the end which we can	 * use.	 */	de_really_used = OCFS2_DIR_REC_LEN(de->name_len);	if (le16_to_cpu(de->rec_len) >= (de_really_used + new_rec_len))	    return 1;	return 0;}/* we don't always have a dentry for what we want to add, so people * like orphan dir can call this instead. * * If you pass me insert_bh, I'll skip the search of the other dir * blocks and put the record in there. */int __ocfs2_add_entry(handle_t *handle,		      struct inode *dir,		      const char *name, int namelen,		      struct inode *inode, u64 blkno,		      struct buffer_head *parent_fe_bh,		      struct buffer_head *insert_bh){	unsigned long offset;	unsigned short rec_len;	struct ocfs2_dir_entry *de, *de1;	struct ocfs2_dinode *di = (struct ocfs2_dinode *)parent_fe_bh->b_data;	struct super_block *sb = dir->i_sb;	int retval, status;	unsigned int size = sb->s_blocksize;	char *data_start = insert_bh->b_data;	mlog_entry_void();	if (!namelen)		return -EINVAL;	if (OCFS2_I(dir)->ip_dyn_features & OCFS2_INLINE_DATA_FL) {		data_start = di->id2.i_data.id_data;		size = i_size_read(dir);		BUG_ON(insert_bh != parent_fe_bh);	}	rec_len = OCFS2_DIR_REC_LEN(namelen);	offset = 0;	de = (struct ocfs2_dir_entry *) data_start;	while (1) {		BUG_ON((char *)de >= (size + data_start));		/* These checks should've already been passed by the		 * prepare function, but I guess we can leave them		 * here anyway. */		if (!ocfs2_check_dir_entry(dir, de, insert_bh, offset)) {			retval = -ENOENT;			goto bail;		}		if (ocfs2_match(namelen, name, de)) {			retval = -EEXIST;			goto bail;		}		if (ocfs2_dirent_would_fit(de, rec_len)) {			dir->i_mtime = dir->i_ctime = CURRENT_TIME;			retval = ocfs2_mark_inode_dirty(handle, dir, parent_fe_bh);			if (retval < 0) {				mlog_errno(retval);				goto bail;			}			status = ocfs2_journal_access(handle, dir, insert_bh,						      OCFS2_JOURNAL_ACCESS_WRITE);			/* By now the buffer is marked for journaling */			offset += le16_to_cpu(de->rec_len);			if (le64_to_cpu(de->inode)) {				de1 = (struct ocfs2_dir_entry *)((char *) de +					OCFS2_DIR_REC_LEN(de->name_len));				de1->rec_len =					cpu_to_le16(le16_to_cpu(de->rec_len) -					OCFS2_DIR_REC_LEN(de->name_len));				de->rec_len = cpu_to_le16(OCFS2_DIR_REC_LEN(de->name_len));				de = de1;			}			de->file_type = OCFS2_FT_UNKNOWN;			if (blkno) {				de->inode = cpu_to_le64(blkno);				ocfs2_set_de_type(de, inode->i_mode);			} else				de->inode = 0;			de->name_len = namelen;			memcpy(de->name, name, namelen);			dir->i_version++;			status = ocfs2_journal_dirty(handle, insert_bh);			retval = 0;			goto bail;		}		offset += le16_to_cpu(de->rec_len);		de = (struct ocfs2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len));	}	/* when you think about it, the assert above should prevent us	 * from ever getting here. */	retval = -ENOSPC;bail:	mlog_exit(retval);	return retval;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -