📄 namei.c
字号:
/* * linux/fs/ext/namei.c * * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) * * from * * linux/fs/minix/namei.c * * Copyright (C) 1991, 1992 Linus Torvalds */#include <linux/sched.h>#include <linux/ext_fs.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/stat.h>#include <linux/fcntl.h>#include <linux/errno.h>#include <asm/segment.h>/* * comment out this line if you want names > EXT_NAME_LEN chars to be * truncated. Else they will be disallowed. *//* #define NO_TRUNCATE *//* * EXT_DIR_PAD defines the directory entries boundaries * * NOTE: It must be a power of 2 and must be greater or equal than 8 * because a directory entry needs 8 bytes for its fixed part * (4 bytes for the inode, 2 bytes for the entry length and 2 bytes * for the name length) */#define EXT_DIR_PAD 8/* * * EXT_DIR_MIN_SIZE is the minimal size of a directory entry * * During allocations, a directory entry is split into 2 ones * *ONLY* if the size of the unused part is greater than or * equal to EXT_DIR_MIN_SIZE */#define EXT_DIR_MIN_SIZE 12/* * ok, we cannot use strncmp, as the name is not in our data space. * Thus we'll have to use ext_match. No big problem. Match also makes * some sanity tests. * * NOTE! unlike strncmp, ext_match returns 1 for success, 0 for failure. */static int ext_match(int len,const char * name,struct ext_dir_entry * de){ register int same __asm__("ax"); if (!de || !de->inode || len > EXT_NAME_LEN) return 0; /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ if (!len && (de->name[0]=='.') && (de->name[1]=='\0')) return 1; if (len < EXT_NAME_LEN && len != de->name_len) return 0; __asm__("cld\n\t" "repe ; cmpsb\n\t" "setz %%al" :"=a" (same) :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len) :"cx","di","si"); return same;}/* * ext_find_entry() * * finds an entry in the specified directory with the wanted name. It * returns the cache buffer in which the entry was found, and the entry * itself (as a parameter - res_dir). It does NOT read the inode of the * entry - you'll have to do that yourself if you want to. * * addition for the ext file system : this function returns the previous * and next directory entries in the parameters prev_dir and next_dir */static struct buffer_head * ext_find_entry(struct inode * dir, const char * name, int namelen, struct ext_dir_entry ** res_dir, struct ext_dir_entry ** prev_dir, struct ext_dir_entry ** next_dir){ long offset; struct buffer_head * bh; struct ext_dir_entry * de; *res_dir = NULL; if (!dir) return NULL;#ifdef NO_TRUNCATE if (namelen > EXT_NAME_LEN) return NULL;#else if (namelen > EXT_NAME_LEN) namelen = EXT_NAME_LEN;#endif bh = ext_bread(dir,0,0); if (!bh) return NULL; if (prev_dir) *prev_dir = NULL; if (next_dir) *next_dir = NULL; offset = 0; de = (struct ext_dir_entry *) bh->b_data; while (offset < dir->i_size) { if ((char *)de >= BLOCK_SIZE+bh->b_data) { brelse(bh); bh = NULL; bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,0); if (!bh) continue; de = (struct ext_dir_entry *) bh->b_data; if (prev_dir) *prev_dir = NULL; } if (de->rec_len < 8 || de->rec_len % 8 != 0 || de->rec_len < de->name_len + 8 || (((char *) de) + de->rec_len-1 >= BLOCK_SIZE+bh->b_data)) { printk ("ext_find_entry: bad dir entry\n"); printk ("dev=%d, dir=%d, offset=%d, rec_len=%d, name_len=%d\n", dir->i_dev, dir->i_ino, offset, de->rec_len, de->name_len); de = (struct ext_dir_entry *) (bh->b_data+BLOCK_SIZE); offset = ((offset / BLOCK_SIZE) + 1) * BLOCK_SIZE; continue;/* brelse (bh); return NULL; */ } if (ext_match(namelen,name,de)) { *res_dir = de; if (next_dir) if (offset + de->rec_len < dir->i_size && ((char *)de) + de->rec_len < BLOCK_SIZE+bh->b_data) *next_dir = (struct ext_dir_entry *) ((char *) de + de->rec_len); else *next_dir = NULL; return bh; } offset += de->rec_len; if (prev_dir) *prev_dir = de; de = (struct ext_dir_entry *) ((char *) de + de->rec_len); } brelse(bh); return NULL;}int ext_lookup(struct inode * dir,const char * name, int len, struct inode ** result){ int ino; struct ext_dir_entry * de; struct buffer_head * bh; *result = NULL; if (!dir) return -ENOENT; if (!S_ISDIR(dir->i_mode)) { iput(dir); return -ENOENT; } if (!(bh = ext_find_entry(dir,name,len,&de,NULL,NULL))) { iput(dir); return -ENOENT; } ino = de->inode; brelse(bh); if (!(*result = iget(dir->i_sb,ino))) { iput(dir); return -EACCES; } iput(dir); return 0;}/* * ext_add_entry() * * adds a file entry to the specified directory, using the same * semantics as ext_find_entry(). It returns NULL if it failed. * * NOTE!! The inode part of 'de' is left at 0 - which means you * may not sleep between calling this and putting something into * the entry, as someone else might have used it while you slept. */static struct buffer_head * ext_add_entry(struct inode * dir, const char * name, int namelen, struct ext_dir_entry ** res_dir){ int i; long offset; unsigned short rec_len; struct buffer_head * bh; struct ext_dir_entry * de, * de1; *res_dir = NULL; if (!dir) return NULL;#ifdef NO_TRUNCATE if (namelen > EXT_NAME_LEN) return NULL;#else if (namelen > EXT_NAME_LEN) namelen = EXT_NAME_LEN;#endif if (!namelen) return NULL; bh = ext_bread(dir,0,0); if (!bh) return NULL; rec_len = ((8 + namelen + EXT_DIR_PAD - 1) / EXT_DIR_PAD) * EXT_DIR_PAD; offset = 0; de = (struct ext_dir_entry *) bh->b_data; while (1) { if ((char *)de >= BLOCK_SIZE+bh->b_data && offset < dir->i_size) {#ifdef EXTFS_DEBUGprintk ("ext_add_entry: skipping to next block\n");#endif brelse(bh); bh = NULL; bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,0); if (!bh) return NULL; de = (struct ext_dir_entry *) bh->b_data; } if (offset >= dir->i_size) { /* Check that the directory entry fits in the block */ if (offset % BLOCK_SIZE == 0 || (BLOCK_SIZE - (offset % BLOCK_SIZE)) < rec_len) { if ((offset % BLOCK_SIZE) != 0) { /* If the entry does not fit in the block, the remainder of the block becomes an unused entry */ de->inode = 0; de->rec_len = BLOCK_SIZE - (offset & (BLOCK_SIZE - 1)); de->name_len = 0; offset += de->rec_len; dir->i_size += de->rec_len; dir->i_dirt = 1;#if 0 dir->i_ctime = CURRENT_TIME;#endif bh->b_dirt = 1; } brelse (bh); bh = NULL;#ifdef EXTFS_DEBUGprintk ("ext_add_entry : creating next block\n");#endif bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,1); if (!bh) return NULL; /* Other thing to do ??? */ de = (struct ext_dir_entry *) bh->b_data; } /* Allocate the entry */ de->inode=0; de->rec_len = rec_len; dir->i_size += de->rec_len; dir->i_dirt = 1;#if 0 dir->i_ctime = CURRENT_TIME;#endif } if (de->rec_len < 8 || de->rec_len % 4 != 0 || de->rec_len < de->name_len + 8 || (((char *) de) + de->rec_len-1 >= BLOCK_SIZE+bh->b_data)) { printk ("ext_addr_entry: bad dir entry\n"); printk ("dev=%d, dir=%d, offset=%d, rec_len=%d, name_len=%d\n", dir->i_dev, dir->i_ino, offset, de->rec_len, de->name_len); brelse (bh); return NULL; } if (!de->inode && de->rec_len >= rec_len) { if (de->rec_len > rec_len && de->rec_len - rec_len >= EXT_DIR_MIN_SIZE) { /* The found entry is too big : it is split into 2 ones : - the 1st one will be used to hold the name, - the 2nd one is unused */ de1 = (struct ext_dir_entry *) ((char *) de + rec_len); de1->inode = 0; de1->rec_len = de->rec_len - rec_len; de1->name_len = 0; de->rec_len = rec_len; } dir->i_mtime = dir->i_ctime = CURRENT_TIME; de->name_len = namelen; for (i=0; i < namelen ; i++) de->name[i] = name[i]; bh->b_dirt = 1; *res_dir = de; return bh; } offset += de->rec_len; de = (struct ext_dir_entry *) ((char *) de + de->rec_len); } brelse(bh); return NULL;}int ext_create(struct inode * dir,const char * name, int len, int mode, struct inode ** result){ struct inode * inode; struct buffer_head * bh; struct ext_dir_entry * de; *result = NULL; if (!dir) return -ENOENT; inode = ext_new_inode(dir); if (!inode) { iput(dir); return -ENOSPC; } inode->i_op = &ext_file_inode_operations; inode->i_mode = mode; inode->i_dirt = 1; bh = ext_add_entry(dir,name,len,&de); if (!bh) { inode->i_nlink--; inode->i_dirt = 1; iput(inode); iput(dir); return -ENOSPC; } de->inode = inode->i_ino; bh->b_dirt = 1; brelse(bh); iput(dir); *result = inode; return 0;}int ext_mknod(struct inode * dir, const char * name, int len, int mode, int rdev){ struct inode * inode; struct buffer_head * bh; struct ext_dir_entry * de; if (!dir) return -ENOENT; bh = ext_find_entry(dir,name,len,&de,NULL,NULL); if (bh) { brelse(bh); iput(dir); return -EEXIST; } inode = ext_new_inode(dir); if (!inode) { iput(dir); return -ENOSPC; } inode->i_uid = current->euid; inode->i_mode = mode; inode->i_op = NULL; if (S_ISREG(inode->i_mode)) inode->i_op = &ext_file_inode_operations; else if (S_ISDIR(inode->i_mode)) { inode->i_op = &ext_dir_inode_operations; if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; } else if (S_ISLNK(inode->i_mode)) inode->i_op = &ext_symlink_inode_operations; else if (S_ISCHR(inode->i_mode)) inode->i_op = &chrdev_inode_operations; else if (S_ISBLK(inode->i_mode)) inode->i_op = &blkdev_inode_operations; else if (S_ISFIFO(inode->i_mode)) init_fifo(inode); if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = rdev;#if 0 inode->i_mtime = inode->i_atime = CURRENT_TIME;#endif inode->i_dirt = 1; bh = ext_add_entry(dir,name,len,&de); if (!bh) { inode->i_nlink--; inode->i_dirt = 1; iput(inode); iput(dir); return -ENOSPC; } de->inode = inode->i_ino; bh->b_dirt = 1; brelse(bh); iput(dir); iput(inode); return 0;}int ext_mkdir(struct inode * dir, const char * name, int len, int mode){ struct inode * inode; struct buffer_head * bh, *dir_block; struct ext_dir_entry * de; bh = ext_find_entry(dir,name,len,&de,NULL,NULL); if (bh) { brelse(bh); iput(dir); return -EEXIST; } inode = ext_new_inode(dir); if (!inode) { iput(dir); return -ENOSPC; } inode->i_op = &ext_dir_inode_operations; inode->i_size = 2 * 16; /* Each entry is coded on 16 bytes for "." and ".." - 4 bytes for the inode number, - 2 bytes for the record length - 2 bytes for the name length - 8 bytes for the name */#if 0 inode->i_mtime = inode->i_atime = CURRENT_TIME;#endif dir_block = ext_bread(inode,0,1); if (!dir_block) { iput(dir); inode->i_nlink--; inode->i_dirt = 1; iput(inode); return -ENOSPC; } de = (struct ext_dir_entry *) dir_block->b_data; de->inode=inode->i_ino; de->rec_len=16; de->name_len=1; strcpy(de->name,"."); de = (struct ext_dir_entry *) ((char *) de + de->rec_len); de->inode = dir->i_ino; de->rec_len=16; de->name_len=2; strcpy(de->name,".."); inode->i_nlink = 2; dir_block->b_dirt = 1; brelse(dir_block); inode->i_mode = S_IFDIR | (mode & 0777 & ~current->umask); if (dir->i_mode & S_ISGID)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -