📄 namei.c
字号:
/*
* linux/fs/msdos/namei.c
*
* Written 1992,1993 by Werner Almesberger
*/
#include <asm/segment.h>
#include <linux/sched.h>
#include <linux/msdos_fs.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
/* MS-DOS "device special files" */
static char *reserved_names[] = {
"CON ","PRN ","NUL ","AUX ",
"LPT1 ","LPT2 ","LPT3 ","LPT4 ",
"COM1 ","COM2 ","COM3 ","COM4 ",
NULL };
/* Characters that are undesirable in an MS-DOS file name */
static char bad_chars[] = "*?<>|\"";
static char bad_if_strict[] = "+=,; ";
/* Formats an MS-DOS file name. Rejects invalid names. */
static int msdos_format_name(char conv,const char *name,int len,char *res,
int dot_dirs)
{
char *walk,**reserved;
unsigned char c;
int space;
if (IS_FREE(name)) return -EINVAL;
if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
if (!dot_dirs) return -EEXIST;
memset(res+1,' ',10);
while (len--) *res++ = '.';
return 0;
}
space = 1; /* disallow names starting with a dot */
c = 0;
for (walk = res; len && walk-res < 8; walk++) {
c = *name++;
len--;
if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL;
if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL;
if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
if (c == '.') break;
space = c == ' ';
*walk = c >= 'a' && c <= 'z' ? c-32 : c;
}
if (space) return -EINVAL;
if (conv == 's' && len && c != '.') {
c = *name++;
len--;
if (c != '.') return -EINVAL;
}
while (c != '.' && len--) c = *name++;
if (c == '.') {
while (walk-res < 8) *walk++ = ' ';
while (len > 0 && walk-res < MSDOS_NAME) {
c = *name++;
len--;
if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
if (conv == 's' && strchr(bad_if_strict,c))
return -EINVAL;
if (c < ' ' || c == ':' || c == '\\' || c == '.')
return -EINVAL;
if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL;
space = c == ' ';
*walk++ = c >= 'a' && c <= 'z' ? c-32 : c;
}
if (space) return -EINVAL;
if (conv == 's' && len) return -EINVAL;
}
while (walk-res < MSDOS_NAME) *walk++ = ' ';
for (reserved = reserved_names; *reserved; reserved++)
if (!strncmp(res,*reserved,8)) return -EINVAL;
return 0;
}
/* Locates a directory entry. */
static int msdos_find(struct inode *dir,const char *name,int len,
struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
{
char msdos_name[MSDOS_NAME];
int res;
if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len,
msdos_name,1)) < 0) return res;
return msdos_scan(dir,msdos_name,bh,de,ino);
}
int msdos_lookup(struct inode *dir,const char *name,int len,
struct inode **result)
{
int ino,res;
struct msdos_dir_entry *de;
struct buffer_head *bh;
struct inode *next;
*result = NULL;
if (!dir) return -ENOENT;
if (!S_ISDIR(dir->i_mode)) {
iput(dir);
return -ENOENT;
}
if (len == 1 && name[0] == '.') {
*result = dir;
return 0;
}
if (len == 2 && name[0] == '.' && name[1] == '.') {
ino = msdos_parent_ino(dir,0);
iput(dir);
if (ino < 0) return ino;
if (!(*result = iget(dir->i_sb,ino))) return -EACCES;
return 0;
}
if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) {
iput(dir);
return res;
}
if (bh) brelse(bh);
/* printk("lookup: ino=%d\n",ino); */
if (!(*result = iget(dir->i_sb,ino))) {
iput(dir);
return -EACCES;
}
if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
iput(*result);
iput(dir);
return -ENOENT;
}
while (MSDOS_I(*result)->i_old) {
next = MSDOS_I(*result)->i_old;
iput(*result);
if (!(*result = iget(next->i_sb,next->i_ino))) {
fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
iput(dir);
return -ENOENT;
}
}
iput(dir);
return 0;
}
/* Creates a directory entry (name is already formatted). */
static int msdos_create_entry(struct inode *dir,char *name,int is_dir,
struct inode **result)
{
struct buffer_head *bh;
struct msdos_dir_entry *de;
int res,ino;
if ((res = msdos_scan(dir,NULL,&bh,&de,&ino)) < 0) {
if (res != -ENOENT) return res;
if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
if ((res = msdos_add_cluster(dir)) < 0) return res;
if ((res = msdos_scan(dir,NULL,&bh,&de,&ino)) < 0) return res;
}
/*
* XXX all times should be set by caller upon successful completion.
*/
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_dirt = 1;
memcpy(de->name,name,MSDOS_NAME);
de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
de->start = 0;
date_unix2dos(dir->i_mtime,&de->time,&de->date);
de->size = 0;
bh->b_dirt = 1;
if ((*result = iget(dir->i_sb,ino)) != NULL)
msdos_read_inode(*result);
brelse(bh);
if (!*result) return -EIO;
(*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
CURRENT_TIME;
(*result)->i_dirt = 1;
return 0;
}
int msdos_create(struct inode *dir,const char *name,int len,int mode,
struct inode **result)
{
struct buffer_head *bh;
struct msdos_dir_entry *de;
char msdos_name[MSDOS_NAME];
int ino,res;
if (!dir) return -ENOENT;
if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len,
msdos_name,0)) < 0) {
iput(dir);
return res;
}
lock_creation();
if (msdos_scan(dir,msdos_name,&bh,&de,&ino) >= 0) {
unlock_creation();
brelse(bh);
iput(dir);
return -EEXIST;
}
res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),result);
unlock_creation();
iput(dir);
return res;
}
#ifdef DEBUG
static void dump_fat(struct super_block *sb,int start)
{
printk("[");
while (start) {
printk("%d ",start);
start = fat_access(sb,start,-1);
if (!start) {
printk("ERROR");
break;
}
if (start == -1) break;
}
printk("]\n");
}
#endif
int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
{
struct buffer_head *bh;
struct msdos_dir_entry *de;
struct inode *inode,*dot;
char msdos_name[MSDOS_NAME];
int ino,res;
if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len,
msdos_name,0)) < 0) {
iput(dir);
return res;
}
lock_creation();
if (msdos_scan(dir,msdos_name,&bh,&de,&ino) >= 0) {
unlock_creation();
brelse(bh);
iput(dir);
return -EEXIST;
}
if ((res = msdos_create_entry(dir,msdos_name,1,&inode)) < 0) {
unlock_creation();
iput(dir);
return res;
}
dir->i_nlink++;
inode->i_nlink = 2; /* no need to mark them dirty */
MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
if ((res = msdos_add_cluster(inode)) < 0) goto mkdir_error;
if ((res = msdos_create_entry(inode,MSDOS_DOT,1,&dot)) < 0)
goto mkdir_error;
dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
dot->i_nlink = inode->i_nlink;
dot->i_dirt = 1;
iput(dot);
if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,&dot)) < 0)
goto mkdir_error;
unlock_creation();
dot->i_size = dir->i_size;
MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
dot->i_nlink = dir->i_nlink;
dot->i_dirt = 1;
MSDOS_I(inode)->i_busy = 0;
iput(dot);
iput(inode);
iput(dir);
return 0;
mkdir_error:
iput(inode);
if (msdos_rmdir(dir,name,len) < 0)
fs_panic(dir->i_sb,"rmdir in mkdir failed");
unlock_creation();
return res;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -