diff options
Diffstat (limited to 'fs')
112 files changed, 3886 insertions, 5567 deletions
diff --git a/fs/Config.in b/fs/Config.in index a9f922d8a..cfc601630 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -5,26 +5,6 @@ mainmenu_option next_comment comment 'Filesystems' bool 'Quota support' CONFIG_QUOTA -bool 'Preload dcache entries in readdir() [ALPHA, currently dangerous!]' CONFIG_DCACHE_PRELOAD -bool 'Include support for omirr online mirror' CONFIG_OMIRR -bool 'Translate filename suffixes' CONFIG_TRANS_NAMES -if [ "$CONFIG_TRANS_NAMES" = "y" ]; then - bool ' Restrict translation to specific gid' CONFIG_TRANS_RESTRICT - if [ "$CONFIG_TRANS_RESTRICT" = "y" ]; then - int ' Enter gid to compile in' CONFIG_TRANS_GID 4 - fi - bool ' Translate nodename' CONFIG_TR_NODENAME - bool ' Translate compiled-in kernelname' CONFIG_TR_KERNNAME - if [ "$CONFIG_TR_KERNNAME" = "y" ]; then - string ' Enter kernelname string to compile in' CONFIG_KERNNAME banana - fi - bool ' Translate compiled-in kerneltype' CONFIG_TR_KERNTYPE - if [ "$CONFIG_TR_KERNTYPE" = "y" ]; then - string ' Enter kerneltype string to compile in' CONFIG_KERNTYPE default - fi - bool ' Translate machine type' CONFIG_TR_MACHINE - bool ' Translate sysname' CONFIG_TR_SYSNAME -fi tristate 'Minix fs support' CONFIG_MINIX_FS tristate 'Second extended fs support' CONFIG_EXT2_FS diff --git a/fs/affs/Changes b/fs/affs/Changes index a65dc326a..b19c24ae6 100644 --- a/fs/affs/Changes +++ b/fs/affs/Changes @@ -13,11 +13,25 @@ Known bugs: reads basically work (but all files are of size 0). Alas, I've got no alpha to debug. :-( - If an affs mounted filesystem is exported via - nfs, it cannot be written to. No networking to - test that, either. :-( + nfs, it cannot be written to. + As soon as I have my network up and running, I'll + try to fix this. +- The partition checker (drivers/block/genhd.c) + doesn't work with devices which have 256 byte + blocks (some very old SCSI drives). Please direct bug reports to: hjw@zvw.de +Version 3.5 +----------- + +- Extension block caches are now allocated on + demand instead of when a file is opened, as + files can be read and written without opening + them (e. g. the loopback device does this). + +- Removed an unused function. + Version 3.4 ----------- @@ -80,7 +94,7 @@ Version 3.0 interface in Linux 1.3. - Write support. - Support for hard and symbolic links. -- Lots of things I remeber even less ... +- Lots of things I remember even less ... Version 2.0 ----------- diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c index 4afd66e49..7ddb54a62 100644 --- a/fs/affs/amigaffs.c +++ b/fs/affs/amigaffs.c @@ -26,27 +26,6 @@ static char ErrorBuffer[256]; * */ -/* Find the next used hash entry at or after *HASH_POS in a directory's hash - table. *HASH_POS is assigned that entry's number. DIR_DATA points to - the directory header block in memory. If there are no more entries, - 0 is returned. Otherwise, the key number in the next used hash slot - is returned. */ - -static int -affs_find_next_hash_entry(int hsize, void *dir_data, int *hash_pos) -{ - struct dir_front *dir_front = dir_data; - int i; - - for (i = *hash_pos; i < hsize; i++) - if (dir_front->hashtable[i] != 0) - break; - if (i >= hsize) - return 0; - *hash_pos = i; - return htonl(dir_front->hashtable[i]); -} - /* Set *NAME to point to the file name in a file header block in memory pointed to by FH_DATA. The length of the name is returned. */ diff --git a/fs/affs/file.c b/fs/affs/file.c index 46b10bcb1..7777e4763 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -43,8 +43,8 @@ static long affs_file_write(struct inode *inode, struct file *filp, const char * unsigned long count); static long affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf, unsigned long count); -static int affs_open_file(struct inode *inode, struct file *filp); static int affs_release_file(struct inode *inode, struct file *filp); +static int alloc_ext_cache(struct inode *inode); static struct file_operations affs_file_operations = { NULL, /* lseek - default */ @@ -54,7 +54,7 @@ static struct file_operations affs_file_operations = { NULL, /* poll - default */ NULL, /* ioctl - default */ generic_file_mmap, /* mmap */ - affs_open_file, /* special open is needed */ + NULL, /* no special open */ affs_release_file, /* release */ file_fsync /* brute force, but works */ }; @@ -87,7 +87,7 @@ static struct file_operations affs_file_operations_ofs = { NULL, /* poll - default */ NULL, /* ioctl - default */ NULL, /* mmap */ - affs_open_file, /* special open is needed */ + NULL, /* no special open */ affs_release_file, /* release */ file_fsync /* brute force, but works */ }; @@ -248,9 +248,9 @@ affs_bmap(struct inode *inode, int block) return 0; } if (!inode->u.affs_i.i_ec) { - affs_error(inode->i_sb,"bmap","No extension cache for open file (inode=%lu)", - inode->i_ino); - return 0; + if (alloc_ext_cache(inode)) { + return 0; + } } /* Try to find the requested key in the cache. @@ -582,6 +582,12 @@ affs_file_write(struct inode *inode, struct file *filp, const char *buf, unsigne iput(inode); return -EINVAL; } + if (!inode->u.affs_i.i_ec) { + if (alloc_ext_cache(inode)) { + iput(inode); + return -ENOMEM; + } + } if (filp->f_flags & O_APPEND) { pos = inode->i_size; } else @@ -861,40 +867,6 @@ affs_truncate(struct inode *inode) } static int -affs_open_file(struct inode *inode, struct file *filp) -{ - int error; - u32 key; - int i; - - pr_debug("AFFS: open_file(ino=%lu)\n",inode->i_ino); - - error = 0; - inode->u.affs_i.i_cache_users++; - lock_super(inode->i_sb); - if (!inode->u.affs_i.i_ec) { - inode->u.affs_i.i_ec = (struct ext_cache *)get_free_page(GFP_KERNEL); - if (!inode->u.affs_i.i_ec) { - affs_error(inode->i_sb,"open_file","Cache allocation failed"); - error = ENOMEM; - } else { - /* We only have to initialize non-zero values. - * get_free_page() zeroed the page already. - */ - key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino; - inode->u.affs_i.i_ec->ec[0] = key; - for (i = 0; i < 4; i++) { - inode->u.affs_i.i_ec->kc[i].kc_this_key = key; - inode->u.affs_i.i_ec->kc[i].kc_last = -1; - } - } - } - unlock_super(inode->i_sb); - - return error; -} - -static int affs_release_file(struct inode *inode, struct file *filp) { struct affs_zone *zone; @@ -916,13 +888,35 @@ affs_release_file(struct inode *inode, struct file *filp) unlock_super(inode->i_sb); } } + return 0; +} + +static int +alloc_ext_cache(struct inode *inode) +{ + s32 key; + int i; + lock_super(inode->i_sb); - if (--inode->u.affs_i.i_cache_users == 0) { + if (!inode->u.affs_i.i_ec) { + inode->u.affs_i.i_ec = (struct ext_cache *)get_free_page(GFP_KERNEL); if (inode->u.affs_i.i_ec) { - free_page((unsigned long)inode->u.affs_i.i_ec); - inode->u.affs_i.i_ec = NULL; + /* We only have to initialize non-zero values. + * get_free_page() zeroed the page already. + */ + key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino; + inode->u.affs_i.i_ec->ec[0] = key; + for (i = 0; i < 4; i++) { + inode->u.affs_i.i_ec->kc[i].kc_this_key = key; + inode->u.affs_i.i_ec->kc[i].kc_last = -1; + } } } unlock_super(inode->i_sb); + + if (!inode->u.affs_i.i_ec) { + affs_error(inode->i_sb,"alloc_ext_cache","Cache allocation failed"); + return -ENOMEM; + } return 0; } diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 2805f1ccf..a1e380cce 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -691,7 +691,6 @@ affs_read_inode(struct inode *inode) inode->u.affs_i.i_pa_next = 0; inode->u.affs_i.i_pa_last = 0; inode->u.affs_i.i_ec = NULL; - inode->u.affs_i.i_cache_users = 0; inode->u.affs_i.i_lastblock = -1; inode->i_nlink = 1; inode->i_mode = 0; @@ -870,6 +869,12 @@ static void affs_put_inode(struct inode *inode) { pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",inode->i_ino,inode->i_nlink); + lock_super(inode->i_sb); + if (inode->u.affs_i.i_ec) { + free_page((unsigned long)inode->u.affs_i.i_ec); + inode->u.affs_i.i_ec = NULL; + } + unlock_super(inode->i_sb); if (inode->i_nlink) { return; } @@ -899,7 +904,7 @@ affs_new_inode(const struct inode *dir) return NULL; } - atomic_set(&inode->i_count, 1); + inode->i_count = 1; inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; @@ -921,7 +926,6 @@ affs_new_inode(const struct inode *dir) inode->u.affs_i.i_pa_next = 0; inode->u.affs_i.i_pa_last = 0; inode->u.affs_i.i_ec = NULL; - inode->u.affs_i.i_cache_users = 0; inode->u.affs_i.i_lastblock = -1; insert_inode_hash(inode); diff --git a/fs/affs/namei.c b/fs/affs/namei.c index 5ea649425..ce04df073 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -343,7 +343,7 @@ affs_rmdir(struct inode *dir, const char *name, int len) retval = -ENOTEMPTY; goto rmdir_done; } - if (atomic_read(&inode->i_count) > 1) { + if (inode->i_count > 1) { retval = -EBUSY; goto rmdir_done; } @@ -512,7 +512,7 @@ subdir(struct inode *new_inode, struct inode *old_inode) int ino; int result; - atomic_inc(&new_inode->i_count); + new_inode->i_count++; result = 0; for (;;) { if (new_inode == old_inode) { @@ -566,12 +566,12 @@ start_up: old_bh = affs_find_entry(old_dir,old_name,old_len,&old_ino); if (!old_bh) goto end_rename; - old_inode = __iget(old_dir->i_sb,old_ino,0); + old_inode = iget(old_dir->i_sb,old_ino); if (!old_inode) goto end_rename; new_bh = affs_find_entry(new_dir,new_name,new_len,&new_ino); if (new_bh) { - new_inode = __iget(new_dir->i_sb,new_ino,0); + new_inode = iget(new_dir->i_sb,new_ino); if (!new_inode) { /* What does this mean? */ affs_brelse(new_bh); new_bh = NULL; @@ -592,7 +592,7 @@ start_up: if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode))) goto end_rename; retval = -EBUSY; - if (atomic_read(&new_inode->i_count) > 1) + if (new_inode->i_count > 1) goto end_rename; } if (S_ISDIR(old_inode->i_mode)) { @@ -74,7 +74,7 @@ void inode_setattr(struct inode * inode, struct iattr * attr) if (!fsuser() && !in_group_p(inode->i_gid)) inode->i_mode &= ~S_ISGID; } - inode->i_dirt = 1; + mark_inode_dirty(inode); } } diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h index cc38577aa..f2bb6c339 100644 --- a/fs/autofs/autofs_i.h +++ b/fs/autofs/autofs_i.h @@ -69,10 +69,8 @@ static inline int copy_from_user(void *dst, void *src, unsigned long len) #define AUTOFS_HASH_SIZE 67 -typedef u32 autofs_hash_t; /* Type returned by autofs_hash() */ - struct autofs_dir_ent { - autofs_hash_t hash; + int hash; struct autofs_dir_ent *next; struct autofs_dir_ent **back; char *name; @@ -94,7 +92,7 @@ struct autofs_wait_queue { struct wait_queue *queue; struct autofs_wait_queue *next; /* We use the following to see what we are waiting for */ - autofs_hash_t hash; + int hash; int len; char *name; /* This is for status reporting upon return */ @@ -151,9 +149,8 @@ void autofs_check_waitlist_integrity(struct autofs_sb_info *,char *); /* Hash operations */ -autofs_hash_t autofs_hash(const char *,int); void autofs_initialize_hash(struct autofs_dirhash *); -struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,autofs_hash_t,const char *,int); +struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,struct qstr *); void autofs_hash_insert(struct autofs_dirhash *,struct autofs_dir_ent *); void autofs_hash_delete(struct autofs_dir_ent *); struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *); @@ -176,7 +173,7 @@ struct super_block *autofs_read_super(struct super_block *, void *,int); /* Queue management functions */ -int autofs_wait(struct autofs_sb_info *,autofs_hash_t,const char *,int); +int autofs_wait(struct autofs_sb_info *,struct qstr *); int autofs_wait_release(struct autofs_sb_info *,unsigned long,int); void autofs_catatonic_mode(struct autofs_sb_info *); diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index 0f529c900..61900c481 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -34,25 +34,13 @@ static int autofs_dir_readdir(struct inode *inode, struct file *filp, return 1; } -static int autofs_dir_lookup(struct inode *dir, const char *name, int len, - struct inode **result) +/* + * No entries except for "." and "..", both of which are handled by the VFS layer + */ +static int autofs_dir_lookup(struct inode *dir, struct dentry * dentry) { - *result = dir; - if (!len) - return 0; - if (name[0] == '.') { - if (len == 1) - return 0; - if (name[1] == '.' && len == 2) { - /* Return the root directory */ - *result = iget(dir->i_sb,AUTOFS_ROOT_INO); - iput(dir); - return 0; - } - } - *result = NULL; - iput(dir); - return -ENOENT; /* No other entries */ + d_add(dentry, NULL); + return 0; } static struct file_operations autofs_dir_operations = { diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c index 90c18695a..60a3c6933 100644 --- a/fs/autofs/dirhash.c +++ b/fs/autofs/dirhash.c @@ -48,35 +48,23 @@ struct autofs_dir_ent *autofs_expire(struct autofs_dirhash *dh, return (jiffies - ent->last_usage >= timeout) ? ent : NULL; } -/* Adapted from the Dragon Book, page 436 */ -/* This particular hashing algorithm requires autofs_hash_t == u32 */ -autofs_hash_t autofs_hash(const char *name, int len) -{ - autofs_hash_t h = 0; - while ( len-- ) { - h = (h << 4) + (unsigned char) (*name++); - h ^= ((h & 0xf0000000) >> 24); - } - return h; -} - void autofs_initialize_hash(struct autofs_dirhash *dh) { memset(&dh->h, 0, AUTOFS_HASH_SIZE*sizeof(struct autofs_dir_ent *)); dh->expiry_head.exp_next = dh->expiry_head.exp_prev = &dh->expiry_head; } -struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, autofs_hash_t hash, const char *name, int len) +struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, struct qstr *name) { struct autofs_dir_ent *dhn; - DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", hash)); - autofs_say(name,len); + DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", name->hash)); + autofs_say(name->name,name->len); - for ( dhn = dh->h[hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) { - if ( hash == dhn->hash && - len == dhn->len && - !memcmp(name, dhn->name, len) ) + for ( dhn = dh->h[(unsigned) name->hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) { + if ( name->hash == dhn->hash && + name->len == dhn->len && + !memcmp(name->name, dhn->name, name->len) ) break; } @@ -92,7 +80,7 @@ void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent) autofs_init_usage(dh,ent); - dhnp = &dh->h[ent->hash % AUTOFS_HASH_SIZE]; + dhnp = &dh->h[(unsigned) ent->hash % AUTOFS_HASH_SIZE]; ent->next = *dhnp; ent->back = dhnp; *dhnp = ent; diff --git a/fs/autofs/init.c b/fs/autofs/init.c index 4dbb76c85..ed4401ce5 100644 --- a/fs/autofs/init.c +++ b/fs/autofs/init.c @@ -21,7 +21,7 @@ static struct file_system_type autofs_fs_type = { "autofs", - FS_NO_DCACHE, + 0 /* FS_NO_DCACHE doesn't work correctly */, autofs_read_super, NULL }; diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index 20ca0907a..870ff120d 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -19,11 +19,17 @@ #define __NO_VERSION__ #include <linux/module.h> +/* + * Dummy functions - do we ever actually want to do + * something here? + */ static void autofs_put_inode(struct inode *inode) { - if (inode->i_nlink) - return; - inode->i_size = 0; +} + +static void autofs_delete_inode(struct inode *inode) +{ + inode->i_size = 0; } static void autofs_put_super(struct super_block *sb) @@ -35,16 +41,16 @@ static void autofs_put_super(struct super_block *sb) if ( !sbi->catatonic ) autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - lock_super(sb); + lock_super(sb); autofs_hash_nuke(&sbi->dirhash); for ( n = 0 ; n < AUTOFS_MAX_SYMLINKS ; n++ ) { if ( test_bit(n, sbi->symlink_bitmap) ) kfree(sbi->symlink[n].data); } - sb->s_dev = 0; + sb->s_dev = 0; kfree(sb->u.generic_sbp); - unlock_super(sb); + unlock_super(sb); DPRINTK(("autofs: shutting down\n")); @@ -53,95 +59,96 @@ static void autofs_put_super(struct super_block *sb) #endif } -static void autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); +static int autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); static void autofs_read_inode(struct inode *inode); static void autofs_write_inode(struct inode *inode); static struct super_operations autofs_sops = { - autofs_read_inode, - NULL, - autofs_write_inode, - autofs_put_inode, - autofs_put_super, - NULL, - autofs_statfs, - NULL + autofs_read_inode, + autofs_write_inode, + autofs_put_inode, + autofs_delete_inode, + NULL, /* notify_change */ + autofs_put_super, + NULL, /* write_super */ + autofs_statfs, + NULL }; static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, pid_t *pgrp, int *minproto, int *maxproto) { - char *this_char, *value; - - *uid = current->uid; - *gid = current->gid; + char *this_char, *value; + + *uid = current->uid; + *gid = current->gid; *pgrp = current->pgrp; *minproto = *maxproto = AUTOFS_PROTO_VERSION; - *pipefd = -1; + *pipefd = -1; - if ( !options ) return 1; - for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { - if ((value = strchr(this_char,'=')) != NULL) - *value++ = 0; - if (!strcmp(this_char,"fd")) { - if (!value || !*value) - return 1; - *pipefd = simple_strtoul(value,&value,0); - if (*value) - return 1; - } - else if (!strcmp(this_char,"uid")) { - if (!value || !*value) - return 1; - *uid = simple_strtoul(value,&value,0); - if (*value) - return 1; - } - else if (!strcmp(this_char,"gid")) { - if (!value || !*value) - return 1; - *gid = simple_strtoul(value,&value,0); - if (*value) - return 1; - } - else if (!strcmp(this_char,"pgrp")) { - if (!value || !*value) - return 1; - *pgrp = simple_strtoul(value,&value,0); - if (*value) - return 1; - } - else if (!strcmp(this_char,"minproto")) { - if (!value || !*value) - return 1; - *minproto = simple_strtoul(value,&value,0); - if (*value) - return 1; - } - else if (!strcmp(this_char,"maxproto")) { - if (!value || !*value) - return 1; - *maxproto = simple_strtoul(value,&value,0); - if (*value) - return 1; - } - else break; - } - return (*pipefd < 0); + if ( !options ) return 1; + for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { + if ((value = strchr(this_char,'=')) != NULL) + *value++ = 0; + if (!strcmp(this_char,"fd")) { + if (!value || !*value) + return 1; + *pipefd = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"uid")) { + if (!value || !*value) + return 1; + *uid = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"gid")) { + if (!value || !*value) + return 1; + *gid = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"pgrp")) { + if (!value || !*value) + return 1; + *pgrp = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"minproto")) { + if (!value || !*value) + return 1; + *minproto = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"maxproto")) { + if (!value || !*value) + return 1; + *maxproto = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else break; + } + return (*pipefd < 0); } struct super_block *autofs_read_super(struct super_block *s, void *data, - int silent) + int silent) { - int pipefd; + int pipefd; struct autofs_sb_info *sbi; int minproto, maxproto; MOD_INC_USE_COUNT; - lock_super(s); - sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL); + lock_super(s); + sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL); if ( !sbi ) { s->s_dev = 0; MOD_DEC_USE_COUNT; @@ -158,30 +165,31 @@ struct super_block *autofs_read_super(struct super_block *s, void *data, sbi->queues = NULL; memset(sbi->symlink_bitmap, 0, sizeof(u32)*AUTOFS_SYMLINK_BITMAP_LEN); sbi->next_dir_ino = AUTOFS_FIRST_DIR_INO; - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; - s->s_op = &autofs_sops; - unlock_super(s); - if (!(s->s_mounted = iget(s, AUTOFS_ROOT_INO))) { - s->s_dev = 0; + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; + s->s_op = &autofs_sops; + unlock_super(s); + s->s_root = d_alloc_root(iget(s, AUTOFS_ROOT_INO), NULL); + if (!s->s_root) { + s->s_dev = 0; kfree(sbi); - printk("autofs: get root inode failed\n"); + printk("autofs: get root inode failed\n"); MOD_DEC_USE_COUNT; - return NULL; - } + return NULL; + } - if ( parse_options(data,&pipefd,&s->s_mounted->i_uid,&s->s_mounted->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) { - iput(s->s_mounted); - s->s_dev = 0; + if ( parse_options(data,&pipefd,&s->s_root->d_inode->i_uid,&s->s_root->d_inode->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) { + dput(s->s_root); + s->s_dev = 0; kfree(sbi); - printk("autofs: called with bogus options\n"); + printk("autofs: called with bogus options\n"); MOD_DEC_USE_COUNT; - return NULL; - } + return NULL; + } if ( minproto > AUTOFS_PROTO_VERSION || maxproto < AUTOFS_PROTO_VERSION ) { - iput(s->s_mounted); + dput(s->s_root); s->s_dev = 0; kfree(sbi); printk("autofs: kernel does not match daemon version\n"); @@ -193,60 +201,60 @@ struct super_block *autofs_read_super(struct super_block *s, void *data, sbi->pipe = fget(pipefd); if ( !sbi->pipe || !sbi->pipe->f_op || !sbi->pipe->f_op->write ) { if ( sbi->pipe ) { - fput(sbi->pipe, sbi->pipe->f_inode); + fput(sbi->pipe); printk("autofs: pipe file descriptor does not contain proper ops\n"); } else { printk("autofs: could not open pipe file descriptor\n"); } - iput(s->s_mounted); + dput(s->s_root); s->s_dev = 0; kfree(sbi); MOD_DEC_USE_COUNT; return NULL; } - return s; + return s; } -static void autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +static int autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { - struct statfs tmp; + struct statfs tmp; - tmp.f_type = AUTOFS_SUPER_MAGIC; - tmp.f_bsize = 1024; - tmp.f_blocks = 0; - tmp.f_bfree = 0; - tmp.f_bavail = 0; - tmp.f_files = 0; - tmp.f_ffree = 0; - tmp.f_namelen = NAME_MAX; - copy_to_user(buf, &tmp, bufsiz); + tmp.f_type = AUTOFS_SUPER_MAGIC; + tmp.f_bsize = 1024; + tmp.f_blocks = 0; + tmp.f_bfree = 0; + tmp.f_bavail = 0; + tmp.f_files = 0; + tmp.f_ffree = 0; + tmp.f_namelen = NAME_MAX; + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } static void autofs_read_inode(struct inode *inode) { - ino_t ino = inode->i_ino; + ino_t ino = inode->i_ino; unsigned int n; - struct autofs_sb_info *sbi = + struct autofs_sb_info *sbi = (struct autofs_sb_info *) inode->i_sb->u.generic_sbp; - inode->i_op = NULL; - inode->i_mode = 0; - inode->i_nlink = 2; - inode->i_size = 0; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - inode->i_blocks = 0; - inode->i_blksize = 1024; + inode->i_op = NULL; + inode->i_mode = 0; + inode->i_nlink = 2; + inode->i_size = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; - if ( ino == AUTOFS_ROOT_INO ) { - inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; - inode->i_op = &autofs_root_inode_operations; + if ( ino == AUTOFS_ROOT_INO ) { + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; + inode->i_op = &autofs_root_inode_operations; inode->i_uid = inode->i_gid = 0; /* Changed in read_super */ - return; - } + return; + } + + inode->i_uid = inode->i_sb->s_root->d_inode->i_uid; + inode->i_gid = inode->i_sb->s_root->d_inode->i_gid; - inode->i_uid = inode->i_sb->s_mounted->i_uid; - inode->i_gid = inode->i_sb->s_mounted->i_gid; - if ( ino >= AUTOFS_FIRST_SYMLINK && ino < AUTOFS_FIRST_DIR_INO ) { /* Symlink inode - should be in symlink list */ struct autofs_symlink *sl; @@ -273,5 +281,4 @@ static void autofs_read_inode(struct inode *inode) static void autofs_write_inode(struct inode *inode) { - inode->i_dirt = 0; } diff --git a/fs/autofs/root.c b/fs/autofs/root.c index a615ede29..4ecc6520e 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -16,11 +16,11 @@ #include "autofs_i.h" static int autofs_root_readdir(struct inode *,struct file *,void *,filldir_t); -static int autofs_root_lookup(struct inode *,const char *,int,struct inode **); -static int autofs_root_symlink(struct inode *,const char *,int,const char *); -static int autofs_root_unlink(struct inode *,const char *,int); -static int autofs_root_rmdir(struct inode *,const char *,int); -static int autofs_root_mkdir(struct inode *,const char *,int,int); +static int autofs_root_lookup(struct inode *,struct dentry *); +static int autofs_root_symlink(struct inode *,struct dentry *,const char *); +static int autofs_root_unlink(struct inode *,struct dentry *); +static int autofs_root_rmdir(struct inode *,struct dentry *); +static int autofs_root_mkdir(struct inode *,struct dentry *,int); static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); static struct file_operations autofs_root_operations = { @@ -48,6 +48,7 @@ struct inode_operations autofs_root_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -92,167 +93,189 @@ static int autofs_root_readdir(struct inode *inode, struct file *filp, return 0; } -static int autofs_root_lookup(struct inode *dir, const char *name, int len, - struct inode **result) +static int try_to_fill_dentry(struct dentry * dentry, struct super_block * sb, struct autofs_sb_info *sbi) { - struct autofs_sb_info *sbi; + struct inode * inode; struct autofs_dir_ent *ent; - struct inode *res; - autofs_hash_t hash; - int status, oz_mode; + + while (!(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name))) { + int status = autofs_wait(sbi, &dentry->d_name); - DPRINTK(("autofs_root_lookup: name = ")); - autofs_say(name,len); + /* Turn this into a real negative dentry? */ + if (status == -ENOENT) { + dentry->d_flags = 0; + return 0; + } + if (status) + return status; + } - *result = NULL; - if (!dir) - return -ENOENT; - if (!S_ISDIR(dir->i_mode)) { - iput(dir); - return -ENOTDIR; + if (!dentry->d_inode) { + inode = iget(sb, ent->ino); + if (!inode) + return -EACCES; + + dentry->d_inode = inode; } - /* Handle special cases: . and ..; since this is a root directory, - they both point to the inode itself */ - *result = dir; - if (!len) - return 0; - if (name[0] == '.') { - if (len == 1) - return 0; - if (name[1] == '.' && len == 2) - return 0; + if (S_ISDIR(dentry->d_inode->i_mode)) { + while (dentry == dentry->d_mounts) + schedule(); } + dentry->d_flags = 0; + return 0; +} + + +/* + * Revalidate is called on every cache lookup. Some of those + * cache lookups may actually happen while the dentry is not + * yet completely filled in, and revalidate has to delay such + * lookups.. + */ +static struct dentry * autofs_revalidate(struct dentry * dentry) +{ + struct autofs_sb_info *sbi; + struct inode * dir = dentry->d_parent->d_inode; - *result = res = NULL; sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; - hash = autofs_hash(name,len); + /* Incomplete dentry? */ + if (dentry->d_flags) { + if (autofs_oz_mode(sbi)) + return dentry; - oz_mode = autofs_oz_mode(sbi); - DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", current->pid, current->pgrp, sbi->catatonic, oz_mode)); + try_to_fill_dentry(dentry, dir->i_sb, sbi); + return dentry; + } - do { - while ( !(ent = autofs_hash_lookup(&sbi->dirhash,hash,name,len)) ) { - DPRINTK(("lookup failed, pid = %u, pgrp = %u\n", current->pid, current->pgrp)); - - if ( oz_mode ) { - iput(dir); - return -ENOENT; - } else { - status = autofs_wait(sbi,hash,name,len); - DPRINTK(("autofs_wait returned %d\n", status)); - if ( status ) { - iput(dir); - return status; - } - } - } + /* Negative dentry.. Should we time these out? */ + if (!dentry->d_inode) + return dentry; - DPRINTK(("lookup successful, inode = %08x\n", (unsigned int)ent->ino)); + /* We should update the usage stuff here.. */ + return dentry; +} - if (!(res = iget(dir->i_sb,ent->ino))) { - printk("autofs: iget returned null!\n"); - iput(dir); - return -EACCES; - } - - if ( !oz_mode && S_ISDIR(res->i_mode) && res->i_sb == dir->i_sb ) { - /* Not a mount point yet, call 1-800-DAEMON */ - DPRINTK(("autofs: waiting on non-mountpoint dir, inode = %lu, pid = %u, pgrp = %u\n", res->i_ino, current->pid, current->pgrp)); - iput(res); - res = NULL; - status = autofs_wait(sbi,hash,name,len); - if ( status ) { - iput(dir); - return status; - } - } - } while(!res); - autofs_update_usage(&sbi->dirhash,ent); - - *result = res; - iput(dir); +static int autofs_root_lookup(struct inode *dir, struct dentry * dentry) +{ + struct autofs_sb_info *sbi; + struct inode *res; + int oz_mode; + + DPRINTK(("autofs_root_lookup: name = ")); + autofs_say(dentry->d_name.name,dentry->d_name.len); + + if (!S_ISDIR(dir->i_mode)) + return -ENOTDIR; + + res = NULL; + sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; + + oz_mode = autofs_oz_mode(sbi); + DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", current->pid, current->pgrp, sbi->catatonic, oz_mode)); + + /* + * Mark the dentry incomplete, but add it. This is needed so + * that the VFS layer knows about the dentry, and we can count + * on catching any lookups through the revalidate. + * + * Let all the hard work be done by the revalidate function that + * needs to be able to do this anyway.. + * + * We need to do this before we release the directory semaphore. + */ + dentry->d_revalidate = autofs_revalidate; + dentry->d_flags = 1; + d_add(dentry, NULL); + + up(&dir->i_sem); + autofs_revalidate(dentry); + down(&dir->i_sem); return 0; } -static int autofs_root_symlink(struct inode *dir, const char *name, int len, const char *symname) +static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; struct autofs_dirhash *dh = &sbi->dirhash; - autofs_hash_t hash = autofs_hash(name,len); struct autofs_dir_ent *ent; unsigned int n; int slsize; struct autofs_symlink *sl; DPRINTK(("autofs_root_symlink: %s <- ", symname)); - autofs_say(name,len); + autofs_say(dentry->d_name.name,dentry->d_name.len); - if ( !autofs_oz_mode(sbi) ) { - iput(dir); + if ( !autofs_oz_mode(sbi) ) return -EPERM; - } - if ( autofs_hash_lookup(dh,hash,name,len) ) { - iput(dir); + + if ( autofs_hash_lookup(dh, &dentry->d_name) ) return -EEXIST; - } + n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS); - if ( n >= AUTOFS_MAX_SYMLINKS ) { - iput(dir); + if ( n >= AUTOFS_MAX_SYMLINKS ) return -ENOSPC; - } + set_bit(n,sbi->symlink_bitmap); sl = &sbi->symlink[n]; sl->len = strlen(symname); sl->data = kmalloc(slsize = sl->len+1, GFP_KERNEL); if ( !sl->data ) { clear_bit(n,sbi->symlink_bitmap); - iput(dir); return -ENOSPC; } + ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); if ( !ent ) { kfree(sl->data); clear_bit(n,sbi->symlink_bitmap); - iput(dir); return -ENOSPC; } - ent->name = kmalloc(len, GFP_KERNEL); + + ent->name = kmalloc(dentry->d_name.len, GFP_KERNEL); if ( !ent->name ) { kfree(sl->data); kfree(ent); clear_bit(n,sbi->symlink_bitmap); - iput(dir); return -ENOSPC; } + memcpy(sl->data,symname,slsize); sl->mtime = CURRENT_TIME; ent->ino = AUTOFS_FIRST_SYMLINK + n; - ent->hash = hash; - memcpy(ent->name,name,ent->len = len); + ent->hash = dentry->d_name.hash; + memcpy(ent->name, dentry->d_name.name,ent->len = dentry->d_name.len); autofs_hash_insert(dh,ent); - iput(dir); + d_instantiate(dentry, iget(dir->i_sb,ent->ino)); return 0; } -static int autofs_root_unlink(struct inode *dir, const char *name, int len) +/* + * NOTE! + * + * Normal filesystems would do a "d_delete()" to tell the VFS dcache + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry, which we + * obviously do not want (we're dropping the entry not because it + * doesn't exist, but because it has timed out). + * + * Also see autofs_root_rmdir().. + */ +static int autofs_root_unlink(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; struct autofs_dirhash *dh = &sbi->dirhash; - autofs_hash_t hash = autofs_hash(name,len); struct autofs_dir_ent *ent; unsigned int n; - iput(dir); /* Nothing below can sleep */ - if ( !autofs_oz_mode(sbi) ) return -EPERM; - ent = autofs_hash_lookup(dh,hash,name,len); + ent = autofs_hash_lookup(dh, &dentry->d_name); if ( !ent ) return -ENOENT; @@ -263,75 +286,68 @@ static int autofs_root_unlink(struct inode *dir, const char *name, int len) autofs_hash_delete(ent); clear_bit(n,sbi->symlink_bitmap); kfree(sbi->symlink[n].data); + d_drop(dentry); return 0; } -static int autofs_root_rmdir(struct inode *dir, const char *name, int len) +static int autofs_root_rmdir(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; struct autofs_dirhash *dh = &sbi->dirhash; - autofs_hash_t hash = autofs_hash(name,len); struct autofs_dir_ent *ent; - if ( !autofs_oz_mode(sbi) ) { - iput(dir); + if ( !autofs_oz_mode(sbi) ) return -EPERM; - } - ent = autofs_hash_lookup(dh,hash,name,len); - if ( !ent ) { - iput(dir); + + ent = autofs_hash_lookup(dh, &dentry->d_name); + if ( !ent ) return -ENOENT; - } - if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) { - iput(dir); + + if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) return -ENOTDIR; /* Not a directory */ - } + autofs_hash_delete(ent); dir->i_nlink--; - iput(dir); + d_drop(dentry); return 0; } -static int autofs_root_mkdir(struct inode *dir, const char *name, int len, int mode) +static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; struct autofs_dirhash *dh = &sbi->dirhash; - autofs_hash_t hash = autofs_hash(name,len); struct autofs_dir_ent *ent; - if ( !autofs_oz_mode(sbi) ) { - iput(dir); + if ( !autofs_oz_mode(sbi) ) return -EPERM; - } - ent = autofs_hash_lookup(dh,hash,name,len); - if ( ent ) { - iput(dir); + + ent = autofs_hash_lookup(dh, &dentry->d_name); + if ( ent ) return -EEXIST; - } + if ( sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO ) { printk("autofs: Out of inode numbers -- what the heck did you do??\n"); - iput(dir); return -ENOSPC; } + ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); - if ( !ent ) { - iput(dir); + if ( !ent ) return -ENOSPC; - } - ent->name = kmalloc(len, GFP_KERNEL); + + ent->name = kmalloc(dentry->d_name.len, GFP_KERNEL); if ( !ent->name ) { kfree(ent); - iput(dir); return -ENOSPC; } - ent->hash = hash; - memcpy(ent->name, name, ent->len = len); + + ent->hash = dentry->d_name.hash; + memcpy(ent->name, dentry->d_name.name, ent->len = dentry->d_name.len); ent->ino = sbi->next_dir_ino++; autofs_hash_insert(dh,ent); dir->i_nlink++; - iput(dir); + d_instantiate(dentry, iget(dir->i_sb,ent->ino)); return 0; } diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c index d6ac82ed4..e3ff3d31b 100644 --- a/fs/autofs/symlink.c +++ b/fs/autofs/symlink.c @@ -19,18 +19,21 @@ static int autofs_readlink(struct inode *inode, char *buffer, int buflen) struct autofs_symlink *sl; int len; - if (!S_ISLNK(inode->i_mode)) { - iput(inode); - return -EINVAL; - } sl = (struct autofs_symlink *)inode->u.generic_ip; len = sl->len; if (len > buflen) len = buflen; copy_to_user(buffer,sl->data,len); - iput(inode); return len; } +static struct dentry * autofs_follow_link(struct inode *inode, struct dentry *base) +{ + struct autofs_symlink *sl; + + sl = (struct autofs_symlink *)inode->u.generic_ip; + return lookup_dentry(sl->data, base, 1); +} + struct inode_operations autofs_symlink_inode_operations = { NULL, /* file operations */ NULL, /* create */ @@ -43,6 +46,7 @@ struct inode_operations autofs_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ autofs_readlink, /* readlink */ + autofs_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c index cbe270a53..39beed699 100644 --- a/fs/autofs/waitq.c +++ b/fs/autofs/waitq.c @@ -37,7 +37,7 @@ void autofs_catatonic_mode(struct autofs_sb_info *sbi) wake_up(&wq->queue); wq = nwq; } - fput(sbi->pipe, sbi->pipe->f_inode); /* Close the pipe */ + fput(sbi->pipe); /* Close the pipe */ } static int autofs_write(struct file *file, const void *addr, int bytes) @@ -55,7 +55,7 @@ static int autofs_write(struct file *file, const void *addr, int bytes) old_signal = current->signal; - while ( bytes && (written = file->f_op->write(file->f_inode,file,data,bytes)) > 0 ) { + while ( bytes && (written = file->f_op->write(file->f_dentry->d_inode,file,data,bytes)) > 0 ) { data += written; bytes -= written; } @@ -90,15 +90,15 @@ static void autofs_notify_daemon(struct autofs_sb_info *sbi, struct autofs_wait_ autofs_catatonic_mode(sbi); } -int autofs_wait(struct autofs_sb_info *sbi, autofs_hash_t hash, const char *name, int len) +int autofs_wait(struct autofs_sb_info *sbi, struct qstr * name) { struct autofs_wait_queue *wq; int status; for ( wq = sbi->queues ; wq ; wq = wq->next ) { - if ( wq->hash == hash && - wq->len == len && - wq->name && !memcmp(wq->name,name,len) ) + if ( wq->hash == name->hash && + wq->len == name->len && + wq->name && !memcmp(wq->name,name->name,name->len) ) break; } @@ -108,17 +108,17 @@ int autofs_wait(struct autofs_sb_info *sbi, autofs_hash_t hash, const char *name if ( !wq ) return -ENOMEM; - wq->name = kmalloc(len,GFP_KERNEL); + wq->name = kmalloc(name->len,GFP_KERNEL); if ( !wq->name ) { kfree(wq); return -ENOMEM; } wq->wait_queue_token = autofs_next_wait_queue++; init_waitqueue(&wq->queue); - wq->hash = hash; - wq->len = len; + wq->hash = name->hash; + wq->len = name->len; wq->status = -EINTR; /* Status return if interrupted */ - memcpy(wq->name, name, len); + memcpy(wq->name, name->name, name->len); wq->next = sbi->queues; sbi->queues = wq; diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index 394f41eb1..c5c7998cf 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -79,6 +79,7 @@ if (file.f_op->llseek) { \ static inline int do_aout_core_dump(long signr, struct pt_regs * regs) { + struct dentry * dentry = NULL; struct inode * inode = NULL; struct file file; unsigned short fs; @@ -114,26 +115,20 @@ do_aout_core_dump(long signr, struct pt_regs * regs) #else corefile[4] = '\0'; #endif - if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) { - inode = NULL; + dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC, 0600); + if (IS_ERR(dentry)) { + dentry = NULL; goto end_coredump; } + inode = dentry->d_inode; if (!S_ISREG(inode->i_mode)) goto end_coredump; if (!inode->i_op || !inode->i_op->default_file_ops) goto end_coredump; if (get_write_access(inode)) goto end_coredump; - file.f_mode = 3; - file.f_flags = 0; - file.f_count = 1; - file.f_inode = inode; - file.f_pos = 0; - file.f_reada = 0; - file.f_op = inode->i_op->default_file_ops; - if (file.f_op->open) - if (file.f_op->open(inode,&file)) - goto done_coredump; + if (init_private_file(&file, dentry, 3)) + goto end_coredump; if (!file.f_op->write) goto close_coredump; has_dumped = 1; @@ -214,7 +209,6 @@ do_aout_core_dump(long signr, struct pt_regs * regs) /* Finally dump the task struct. Not be used by gdb, but could be useful */ set_fs(KERNEL_DS); DUMP_WRITE(current,sizeof(*current)); - inode->i_status |= ST_MODIFIED; close_coredump: if (file.f_op->release) file.f_op->release(inode,&file); @@ -222,7 +216,7 @@ done_coredump: put_write_access(inode); end_coredump: set_fs(fs); - iput(inode); + dput(dentry); return has_dumped; } @@ -318,7 +312,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) || N_TRSIZE(ex) || N_DRSIZE(ex) || - bprm->inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { + bprm->dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { return -ENOEXEC; } @@ -332,7 +326,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs } if (N_MAGIC(ex) == ZMAGIC && ex.a_text && - (fd_offset < bprm->inode->i_sb->s_blocksize)) { + (fd_offset < bprm->dentry->d_inode->i_sb->s_blocksize)) { printk(KERN_NOTICE "N_TXTOFF < BLOCK_SIZE. Please convert binary.\n"); return -ENOEXEC; } @@ -372,12 +366,12 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs error = do_mmap(NULL, N_TXTADDR(ex), ex.a_text, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - read_exec(bprm->inode, fd_offset, (char *) N_TXTADDR(ex), + read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex), ex.a_text, 0); error = do_mmap(NULL, N_DATADDR(ex), ex.a_data, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - read_exec(bprm->inode, fd_offset + ex.a_text, (char *) N_DATADDR(ex), + read_exec(bprm->dentry, fd_offset + ex.a_text, (char *) N_DATADDR(ex), ex.a_data, 0); goto beyond_if; } @@ -389,20 +383,20 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs ex.a_text+ex.a_data + PAGE_SIZE - 1, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - read_exec(bprm->inode, fd_offset, (char *) N_TXTADDR(ex), + read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0); #else do_mmap(NULL, 0, ex.a_text+ex.a_data, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - read_exec(bprm->inode, 32, (char *) 0, ex.a_text+ex.a_data, 0); + read_exec(bprm->dentry, 32, (char *) 0, ex.a_text+ex.a_data, 0); #endif } else { if ((ex.a_text & 0xfff || ex.a_data & 0xfff) && (N_MAGIC(ex) != NMAGIC)) printk(KERN_NOTICE "executable not page aligned\n"); - fd = open_inode(bprm->inode, O_RDONLY); + fd = open_dentry(bprm->dentry, O_RDONLY); if (fd < 0) return fd; @@ -412,7 +406,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs do_mmap(NULL, 0, ex.a_text+ex.a_data, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - read_exec(bprm->inode, fd_offset, + read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0); goto beyond_if; } @@ -482,18 +476,21 @@ do_load_aout_library(int fd) { struct file * file; struct exec ex; - struct inode * inode; + struct dentry * dentry; + struct inode * inode; unsigned int len; unsigned int bss; unsigned int start_addr; unsigned long error; file = current->files->fd[fd]; - inode = file->f_inode; if (!file || !file->f_op) return -EACCES; + dentry = file->f_dentry; + inode = dentry->d_inode; + /* Seek into the file */ if (file->f_op->llseek) { if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index ff987e0e8..7629fd0d8 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -134,23 +134,20 @@ create_elf_tables(char *p, int argc, int envc, { elf_caddr_t *argv; elf_caddr_t *envp; - elf_addr_t *sp; + elf_addr_t *sp, *csp; /* - * Force 16 byte alignment here for generality. + * Force 16 byte _final_ alignment here for generality. */ sp = (elf_addr_t *) (~15UL & (unsigned long) p); -#if defined(__mips__) || defined(__sparc__) -{ - elf_addr_t *csp; csp = sp; csp -= exec ? DLINFO_ITEMS*2 : 2; csp -= envc+1; csp -= argc+1; - if (!(((unsigned long) csp) & 4)) - sp--; -} -#endif + csp -= (!ibcs ? 3 : 1); /* argc itself */ + if ((unsigned long)csp & 15UL) { + sp -= (16UL - ((unsigned long)csp & 15UL)) / sizeof(*sp); + } /* * Put the ELF interpreter info on the stack @@ -165,17 +162,17 @@ create_elf_tables(char *p, int argc, int envc, if (exec) { sp -= 11*2; - NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff); - NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr)); - NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum); - NEW_AUX_ENT (3, AT_PAGESZ, ELF_EXEC_PAGESIZE); - NEW_AUX_ENT (4, AT_BASE, interp_load_addr); - NEW_AUX_ENT (5, AT_FLAGS, 0); - NEW_AUX_ENT (6, AT_ENTRY, (elf_addr_t) exec->e_entry); - NEW_AUX_ENT (7, AT_UID, (elf_addr_t) current->uid); - NEW_AUX_ENT (8, AT_EUID, (elf_addr_t) current->euid); - NEW_AUX_ENT (9, AT_GID, (elf_addr_t) current->gid); - NEW_AUX_ENT (10, AT_EGID, (elf_addr_t) current->egid); + NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff); + NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr)); + NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum); + NEW_AUX_ENT (3, AT_PAGESZ, ELF_EXEC_PAGESIZE); + NEW_AUX_ENT (4, AT_BASE, interp_load_addr); + NEW_AUX_ENT (5, AT_FLAGS, 0); + NEW_AUX_ENT (6, AT_ENTRY, (elf_addr_t) exec->e_entry); + NEW_AUX_ENT (7, AT_UID, (elf_addr_t) current->uid); + NEW_AUX_ENT (8, AT_EUID, (elf_addr_t) current->euid); + NEW_AUX_ENT (9, AT_GID, (elf_addr_t) current->gid); + NEW_AUX_ENT (10, AT_EGID, (elf_addr_t) current->egid); } #undef NEW_AUX_ENT @@ -212,7 +209,7 @@ create_elf_tables(char *p, int argc, int envc, an ELF header */ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, - struct inode * interpreter_inode, + struct dentry * interpreter_dentry, unsigned long *interp_load_addr) { struct file * file; @@ -234,8 +231,9 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, if ((interp_elf_ex->e_type != ET_EXEC && interp_elf_ex->e_type != ET_DYN) || !elf_check_arch(interp_elf_ex->e_machine) || - (!interpreter_inode->i_op || - !interpreter_inode->i_op->default_file_ops->mmap)){ + (!interpreter_dentry->d_inode->i_op || + !interpreter_dentry->d_inode->i_op->default_file_ops->mmap)){ + #ifdef DEBUG_ELF printk("bad e_type %d ", interp_elf_ex->e_type); #endif @@ -265,7 +263,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, return ~0UL; } - retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff, + retval = read_exec(interpreter_dentry, interp_elf_ex->e_phoff, (char *) elf_phdata, sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, 1); @@ -274,7 +272,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, return retval; } - elf_exec_fileno = open_inode(interpreter_inode, O_RDONLY); + elf_exec_fileno = open_dentry(interpreter_dentry, O_RDONLY); if (elf_exec_fileno < 0) { kfree(elf_phdata); return ~0UL; @@ -366,7 +364,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, } static unsigned long load_aout_interp(struct exec * interp_ex, - struct inode * interpreter_inode) + struct dentry * interpreter_dentry) { int retval; unsigned long elf_entry; @@ -381,13 +379,13 @@ static unsigned long load_aout_interp(struct exec * interp_ex, do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - retval = read_exec(interpreter_inode, 32, (char *) 0, + retval = read_exec(interpreter_dentry, 32, (char *) 0, interp_ex->a_text+interp_ex->a_data, 0); } else if (N_MAGIC(*interp_ex) == ZMAGIC || N_MAGIC(*interp_ex) == QMAGIC) { do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - retval = read_exec(interpreter_inode, + retval = read_exec(interpreter_dentry, N_TXTOFF(*interp_ex) , (char *) N_TXTADDR(*interp_ex), interp_ex->a_text+interp_ex->a_data, 0); @@ -422,7 +420,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) struct elfhdr interp_elf_ex; struct file * file; struct exec interp_ex; - struct inode *interpreter_inode; + struct dentry *interpreter_dentry; unsigned long load_addr; int load_addr_set = 0; unsigned int interpreter_type = INTERPRETER_NONE; @@ -456,8 +454,8 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) || (! elf_check_arch(elf_ex.e_machine)) || - (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || - !bprm->inode->i_op->default_file_ops->mmap)){ + (!bprm->dentry->d_inode->i_op || !bprm->dentry->d_inode->i_op->default_file_ops || + !bprm->dentry->d_inode->i_op->default_file_ops->mmap)){ return -ENOEXEC; } @@ -475,7 +473,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) return -ENOMEM; } - retval = read_exec(bprm->inode, elf_ex.e_phoff, (char *) elf_phdata, + retval = read_exec(bprm->dentry, elf_ex.e_phoff, (char *) elf_phdata, elf_ex.e_phentsize * elf_ex.e_phnum, 1); if (retval < 0) { kfree (elf_phdata); @@ -487,7 +485,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) elf_bss = 0; elf_brk = 0; - elf_exec_fileno = open_inode(bprm->inode, O_RDONLY); + elf_exec_fileno = open_dentry(bprm->dentry, O_RDONLY); if (elf_exec_fileno < 0) { kfree (elf_phdata); @@ -525,7 +523,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) return -ENOMEM; } - retval = read_exec(bprm->inode,elf_ppnt->p_offset, + retval = read_exec(bprm->dentry,elf_ppnt->p_offset, elf_interpreter, elf_ppnt->p_filesz, 1); /* If the program interpreter is one of these two, @@ -540,13 +538,14 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) if (retval >= 0) { old_fs = get_fs(); /* This could probably be optimized */ set_fs(get_ds()); - retval = open_namei(elf_interpreter, 0, 0, - &interpreter_inode, NULL); + interpreter_dentry = open_namei(elf_interpreter, 0, 0); set_fs(old_fs); + if (IS_ERR(interpreter_dentry)) + retval = PTR_ERR(interpreter_dentry); } if (retval >= 0) - retval = read_exec(interpreter_inode,0,bprm->buf,128, 1); + retval = read_exec(interpreter_dentry,0,bprm->buf,128, 1); if (retval >= 0) { interp_ex = *((struct exec *) bprm->buf); /* exec-header */ @@ -682,13 +681,13 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) if (elf_interpreter) { if (interpreter_type & 1) elf_entry = load_aout_interp(&interp_ex, - interpreter_inode); + interpreter_dentry); else if (interpreter_type & 2) elf_entry = load_elf_interp(&interp_elf_ex, - interpreter_inode, + interpreter_dentry, &interp_load_addr); - iput(interpreter_inode); + dput(interpreter_dentry); kfree(elf_interpreter); if (elf_entry == ~0UL) { @@ -716,8 +715,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) __MOD_INC_USE_COUNT(current->binfmt->module); #ifndef VM_STACK_FLAGS - current->executable = bprm->inode; - atomic_inc(&bprm->inode->i_count); + current->executable = dget(bprm->dentry); #endif #ifdef LOW_ELF_STACK current->start_stack = bprm->p = elf_stack - 4; @@ -802,7 +800,8 @@ do_load_elf_library(int fd){ struct file * file; struct elfhdr elf_ex; struct elf_phdr *elf_phdata = NULL; - struct inode * inode; + struct dentry * dentry; + struct inode * inode; unsigned long len; int elf_bss; int retval; @@ -812,7 +811,8 @@ do_load_elf_library(int fd){ len = 0; file = current->files->fd[fd]; - inode = file->f_inode; + dentry = file->f_dentry; + inode = dentry->d_inode; elf_bss = 0; if (!file || !file->f_op) @@ -851,7 +851,7 @@ do_load_elf_library(int fd){ if (elf_phdata == NULL) return -ENOMEM; - retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata, + retval = read_exec(dentry, elf_ex.e_phoff, (char *) elf_phdata, sizeof(struct elf_phdr) * elf_ex.e_phnum, 1); j = 0; @@ -923,14 +923,13 @@ static int load_elf_library(int fd) */ static int dump_write(struct file *file, const void *addr, int nr) { - file->f_inode->i_status |= ST_MODIFIED; - return file->f_op->write(file->f_inode, file, addr, nr) == nr; + return file->f_op->write(file->f_dentry->d_inode, file, addr, nr) == nr; } static int dump_seek(struct file *file, off_t off) { if (file->f_op->llseek) { - if (file->f_op->llseek(file->f_inode, file, off, 0) != off) + if (file->f_op->llseek(file->f_dentry->d_inode, file, off, 0) != off) return 0; } else file->f_pos = off; @@ -1045,6 +1044,7 @@ static int elf_core_dump(long signr, struct pt_regs * regs) { int has_dumped = 0; struct file file; + struct dentry *dentry; struct inode *inode; unsigned short fs; char corefile[6+sizeof(current->comm)]; @@ -1118,24 +1118,18 @@ static int elf_core_dump(long signr, struct pt_regs * regs) #else corefile[4] = '\0'; #endif - if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) { - inode = NULL; + dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC, 0600); + if (IS_ERR(dentry)) { + dentry = NULL; goto end_coredump; } + inode = dentry->d_inode; if (!S_ISREG(inode->i_mode)) goto end_coredump; if (!inode->i_op || !inode->i_op->default_file_ops) goto end_coredump; - file.f_mode = 3; - file.f_flags = 0; - file.f_count = 1; - file.f_inode = inode; - file.f_pos = 0; - file.f_reada = 0; - file.f_op = inode->i_op->default_file_ops; - if (file.f_op->open) - if (file.f_op->open(inode,&file)) - goto end_coredump; + if (init_private_file(&file, dentry, 3)) + goto end_coredump; if (!file.f_op->write) goto close_coredump; has_dumped = 1; @@ -1326,7 +1320,7 @@ static int elf_core_dump(long signr, struct pt_regs * regs) end_coredump: set_fs(fs); - iput(inode); + dput(dentry); #ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; #endif diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c index 7bd71f212..133586e69 100644 --- a/fs/binfmt_em86.c +++ b/fs/binfmt_em86.c @@ -23,6 +23,7 @@ static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs) { char *interp, *i_name, *i_arg; + struct dentry * dentry; int retval; struct elfhdr elf_ex; @@ -39,14 +40,14 @@ static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs) if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) || (!((elf_ex.e_machine == EM_386) || (elf_ex.e_machine == EM_486))) || - (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || - !bprm->inode->i_op->default_file_ops->mmap)){ + (!bprm->dentry->d_inode->i_op || !bprm->dentry->d_inode->i_op->default_file_ops || + !bprm->dentry->d_inode->i_op->default_file_ops->mmap)){ return -ENOEXEC; } bprm->sh_bang++; /* Well, the bang-shell is implicit... */ - iput(bprm->inode); - bprm->dont_iput = 1; + dput(bprm->dentry); + bprm->dentry = NULL; /* Unlike in the script case, we don't have to do any hairy * parsing to find our interpreter... it's hardcoded! @@ -79,14 +80,17 @@ static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs) * Note that we use open_namei() as the name is now in kernel * space, and we don't need to copy it. */ - retval = open_namei(interp, 0, 0, &bprm->inode, NULL); - if (retval) - return retval; - bprm->dont_iput=0; - retval=prepare_binprm(bprm); - if(retval<0) + dentry = open_namei(interp, 0, 0); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + bprm->dentry = dentry; + + retval = prepare_binprm(bprm); + if (retval < 0) return retval; - return search_binary_handler(bprm,regs); + + return search_binary_handler(bprm, regs); } static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs) diff --git a/fs/binfmt_java.c b/fs/binfmt_java.c index fcf664c5d..41e66a21a 100644 --- a/fs/binfmt_java.c +++ b/fs/binfmt_java.c @@ -28,7 +28,9 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs) char *i_name; int len; int retval; + struct dentry * dentry; unsigned char *ucp = (unsigned char *) bprm->buf; + if ((ucp[0] != 0xca) || (ucp[1] != 0xfe) || (ucp[2] != 0xba) || (ucp[3] != 0xbe)) return -ENOEXEC; @@ -42,8 +44,8 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs) bprm->java = 1; - iput(bprm->inode); - bprm->dont_iput=1; + dput(bprm->dentry); + bprm->dentry = NULL; /* * Set args: [0] the name of the java interpreter @@ -75,15 +77,17 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs) if (!bprm->p) return -E2BIG; /* - * OK, now restart the process with the interpreter's inode. + * OK, now restart the process with the interpreter's dentry. */ bprm->filename = binfmt_java_interpreter; - retval = open_namei(binfmt_java_interpreter, 0, 0, &bprm->inode, NULL); - if (retval) + dentry = open_namei(binfmt_java_interpreter, 0, 0); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) return retval; - bprm->dont_iput=0; - retval=prepare_binprm(bprm); - if(retval<0) + + bprm->dentry = dentry; + retval = prepare_binprm(bprm); + if (retval < 0) return retval; return search_binary_handler(bprm,regs); @@ -92,12 +96,14 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs) static int do_load_applet(struct linux_binprm *bprm,struct pt_regs *regs) { char *i_name; + struct dentry * dentry; int retval; + if (strncmp (bprm->buf, "<!--applet", 10)) return -ENOEXEC; - iput(bprm->inode); - bprm->dont_iput=1; + dput(bprm->dentry); + bprm->dentry = NULL; /* * Set args: [0] the name of the appletviewer @@ -118,15 +124,17 @@ static int do_load_applet(struct linux_binprm *bprm,struct pt_regs *regs) if (!bprm->p) return -E2BIG; /* - * OK, now restart the process with the interpreter's inode. + * OK, now restart the process with the interpreter's dentry. */ bprm->filename = binfmt_java_appletviewer; - retval = open_namei(binfmt_java_appletviewer, 0, 0, &bprm->inode, NULL); - if (retval) + dentry = open_namei(binfmt_java_appletviewer, 0, 0); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) return retval; - bprm->dont_iput=0; - retval=prepare_binprm(bprm); - if(retval<0) + + bprm->dentry = dentry; + retval = prepare_binprm(bprm); + if (retval < 0) return retval; return search_binary_handler(bprm,regs); diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 28dced394..ffca300d9 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -7,9 +7,11 @@ * a specified wrapper. This should obsolete binfmt_java, binfmt_em86 and * binfmt_mz. * - * 25.4.97 first version - * [...] - * 19.5.97 cleanup + * 1997-04-25 first version + * [...] + * 1997-05-19 cleanup + * 1997-06-26 hpa: pass the real filename rather than argv[0] + * 1997-06-30 minor cleanup */ #include <linux/module.h> @@ -48,7 +50,7 @@ struct binfmt_entry { #define ENTRY_ENABLED 1 /* the old binfmt_entry.enabled */ #define ENTRY_MAGIC 8 /* not filename detection */ -#define ENTRY_STRIP_EXT 32 /* strip of last filename extension */ +#define ENTRY_STRIP_EXT 32 /* strip off last filename extension */ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs); static void entry_proc_cleanup(struct binfmt_entry *e); @@ -85,7 +87,6 @@ static void clear_entry(int id) *ep = e->next; entry_proc_cleanup(e); kfree(e); - MOD_DEC_USE_COUNT; } write_unlock(&entries_lock); } @@ -102,7 +103,6 @@ static void clear_entries(void) entries = entries->next; entry_proc_cleanup(e); kfree(e); - MOD_DEC_USE_COUNT; } write_unlock(&entries_lock); } @@ -157,6 +157,7 @@ static struct binfmt_entry *check_file(struct linux_binprm *bprm) static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) { struct binfmt_entry *fmt; + struct dentry * dentry; char iname[128]; char *iname_addr = iname, *p; int retval, fmt_flags = 0; @@ -180,17 +181,16 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) goto _ret; } - iput(bprm->inode); - bprm->dont_iput = 1; + dput(bprm->dentry); + bprm->dentry = NULL; /* Build args for interpreter */ if ((fmt_flags & ENTRY_STRIP_EXT) && - (p = strrchr(bprm->filename, '.'))) { + (p = strrchr(bprm->filename, '.'))) *p = '\0'; - remove_arg_zero(bprm); - bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2); - bprm->argc++; - } + remove_arg_zero(bprm); + bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2); + bprm->argc++; bprm->p = copy_strings(1, &iname_addr, bprm->page, bprm->p, 2); bprm->argc++; if (!bprm->p) { @@ -199,11 +199,14 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) } bprm->filename = iname; /* for binfmt_script */ - if ((retval = open_namei(iname, 0, 0, &bprm->inode, NULL))) + dentry = open_namei(iname, 0, 0); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto _ret; - bprm->dont_iput = 0; + bprm->dentry = dentry; - if ((retval = prepare_binprm(bprm)) >= 0) + retval = prepare_binprm(bprm); + if (retval >= 0) retval = search_binary_handler(bprm, regs); _ret: MOD_DEC_USE_COUNT; @@ -322,7 +325,7 @@ static int proc_write_register(struct file *file, const char *buffer, entries = e; write_unlock(&entries_lock); - return count; + err = count; _err: MOD_DEC_USE_COUNT; return err; @@ -499,6 +502,7 @@ void cleanup_module(void) unregister_binfmt(&misc_format); remove_proc_entry("register", bm_dir); remove_proc_entry("status", bm_dir); + clear_entries(); remove_proc_entry("sys/fs/binfmt_misc", NULL); } #endif diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c index 1bd2f0d10..5a38cf545 100644 --- a/fs/binfmt_script.c +++ b/fs/binfmt_script.c @@ -15,8 +15,10 @@ static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs) { char *cp, *i_name, *i_name_start, *i_arg; + struct dentry * dentry; char interp[128]; int retval; + if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!') || (bprm->sh_bang)) return -ENOEXEC; /* @@ -25,8 +27,8 @@ static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs) */ bprm->sh_bang++; - iput(bprm->inode); - bprm->dont_iput=1; + dput(bprm->dentry); + bprm->dentry = NULL; bprm->buf[127] = '\0'; if ((cp = strchr(bprm->buf, '\n')) == NULL) @@ -75,14 +77,15 @@ static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs) if (!bprm->p) return -E2BIG; /* - * OK, now restart the process with the interpreter's inode. + * OK, now restart the process with the interpreter's dentry. */ - retval = open_namei(interp, 0, 0, &bprm->inode, NULL); - if (retval) - return retval; - bprm->dont_iput=0; - retval=prepare_binprm(bprm); - if(retval<0) + dentry = open_namei(interp, 0, 0); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + bprm->dentry = dentry; + retval = prepare_binprm(bprm); + if (retval < 0) return retval; return search_binary_handler(bprm,regs); } diff --git a/fs/buffer.c b/fs/buffer.c index bd06972f3..3e1b5cc35 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -289,16 +289,35 @@ int file_fsync (struct inode *inode, struct file *filp) asmlinkage int sys_fsync(unsigned int fd) { struct file * file; + struct dentry * dentry; struct inode * inode; - int err = 0; + int err; lock_kernel(); - if (fd>=NR_OPEN || !(file=current->files->fd[fd]) || !(inode=file->f_inode)) - err = -EBADF; - else if (!file->f_op || !file->f_op->fsync) - err = -EINVAL; - else if (file->f_op->fsync(inode,file)) - err = -EIO; + err = -EBADF; + + if (fd >= NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if (!file) + goto out; + + dentry = file->f_dentry; + if (!dentry) + goto out; + + inode = dentry->d_inode; + if (!inode) + goto out; + + err = -EINVAL; + if (!file->f_op || !file->f_op->fsync) + goto out; + + err = file->f_op->fsync(inode,file); + +out: unlock_kernel(); return err; } @@ -306,20 +325,35 @@ asmlinkage int sys_fsync(unsigned int fd) asmlinkage int sys_fdatasync(unsigned int fd) { struct file * file; + struct dentry * dentry; struct inode * inode; - int err = -EBADF; + int err; lock_kernel(); - if (fd>=NR_OPEN || !(file=current->files->fd[fd]) || !(inode=file->f_inode)) + err = -EBADF; + + if (fd >= NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if (!file) + goto out; + + dentry = file->f_dentry; + if (!dentry) goto out; + + inode = dentry->d_inode; + if (!inode) + goto out; + err = -EINVAL; if (!file->f_op || !file->f_op->fsync) goto out; + /* this needs further work, at the moment it is identical to fsync() */ - if (file->f_op->fsync(inode,file)) - err = -EIO; - else - err = 0; + err = file->f_op->fsync(inode,file); + out: unlock_kernel(); return err; @@ -495,17 +529,18 @@ static inline void insert_into_queues(struct buffer_head * bh) static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size) { - struct buffer_head * tmp; - - for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next) - if (tmp->b_blocknr == block && tmp->b_dev == dev) { - if (tmp->b_size == size) - return tmp; + struct buffer_head * next; - printk("VFS: Wrong blocksize on device %s\n", - kdevname(dev)); - return NULL; - } + next = hash(dev,block); + for (;;) { + struct buffer_head *tmp = next; + if (!next) + break; + next = tmp->b_next; + if (tmp->b_blocknr != block || tmp->b_size != size || tmp->b_dev != dev) + continue; + return tmp; + } return NULL; } @@ -518,10 +553,11 @@ static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size) */ struct buffer_head * get_hash_table(kdev_t dev, int block, int size) { - struct buffer_head * bh; - for (;;) { - if (!(bh=find_buffer(dev,block,size))) + struct buffer_head * bh; + + bh=find_buffer(dev,block,size); + if (!bh) return NULL; bh->b_count++; wait_on_buffer(bh); @@ -1610,6 +1646,7 @@ asmlinkage int sync_old_buffers(void) next->b_count--; } } + run_task_queue(&tq_disk); #ifdef DEBUG if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount); printk("Wrote %d/%d buffers\n", nwritten, ndirty); diff --git a/fs/dcache.c b/fs/dcache.c index 0472487e0..395aed829 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -5,1035 +5,341 @@ * (C) 1997 Thomas Schoebel-Theuer */ -/* The new dcache is exclusively called from the VFS, not from - * the specific fs'es any more. Despite having the same name as in the - * old code, it has less to do with it. - * - * It serves many purposes: - * - * 1) Any inode that has been retrieved with lookup() and is in use - * (i_count>0), has access to its full absolute path name, by going - * to inode->i_dentry and then recursively following the entry->d_parent - * chain. Use d_path() as predefined method for that. - * You may find out the corresponding inode belonging to - * a dentry by calling d_inode(). This can be used as an easy way for - * determining .. and its absolute pathname, an old UNIX problem that - * deserved a solution for a long time. - * Note that hardlinked inodes may have multiple dentries assigned to - * (via the d_next chain), reflecting multiple alias pathnames. - * - * 2) If not disabled by filesystem types specifying FS_NO_DCACHE, - * the dentries of unused (aged) inodes are retained for speeding up - * lookup()s, by allowing hashed inquiry starting from the dentry of - * the parent directory. - * - * 3) It can remeber so-called "negative entries", that is dentries for - * pathnames that are known to *not* exist, so unneccessary repeated - * lookup()s for non-existant names can be saved. - * - * 4) It provides a means for keeping deleted files (inode->i_nlink==0) - * accessible in the so-called *basket*. Inodes in the basket have been - * removed with unlink() while being in use (i_count>0), so they would - * normally use up space on the disk and be accessile through their - * filedescriptor, but would not be accessible for lookup() any more. - * The basket simply keeps such files in the dcache (for potential - * dcache lookup) until they are either eventually removed completely, - * or transferred to the second-level basket, the so-called *ibasket*. - * The ibasket is implemented in the new inode code, on request of - * filesystem types that have the flag FS_IBASKET set, and proliferates - * the unlinked files when i_count has gone to zero, at least as long - * as there is space on the disk and enough inodes remain available - * and no umount() has started. - * - * 5) Preliminary dentries can be added by readdir(). While normal dentries - * directly point to the inode via u.d_inode only the inode number is - * known from readdir(), but not more. They can be converted to - * normal dentries by using d_inode(). - */ - /* * Notes on the allocation strategy: * - * The dcache is a full slave cache of the inodes. Whenever an inode - * is cleared, all the dentries associated with it will recursively - * disappear. dentries have no own reference counting; this has to - * be obeyed for SMP. - * If directories could go out of inode cache while - * successors are alive, this would interrupt the d_parent chain of - * the live successors. To prevent this without using zombies, all - * directories are thus prevented from __iput() as long as successors - * are alive. + * The dcache is a master of the icache - whenever a dcache entry + * exists, the inode will always exist. "iput()" is done either when + * the dcache entry is deleted or garbage collected. */ -#include <linux/config.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/fs.h> -#include <linux/dalloc.h> -#include <linux/dlists.h> - -/* this should be removed after the beta phase */ -/* #define DEBUG */ -/*#undef DEBUG*/ -/* #define DEBUG_DDIR_COUNT */ - -#define D_HASHSIZE 64 - -/* local flags for d_flag */ -#define D_DIR 32 -#define D_HASHED 64 -#define D_ZOMBIE 128 -#define D_PRELIMINARY 256 -#define D_INC_DDIR 512 - -/* local flags for d_del() */ -#define D_RECURSIVE 4 -#define D_NO_FREE 8 - -/* adjust these constants if you know a probability distribution ... */ -#define D_SMALL 16 -#define D_MEDIUM 64 -#define D_LARGE 256 -#define D_HUGE D_MAXLEN +#include <linux/malloc.h> -#define BASE_DHEADER(x) (struct dheader*)((unsigned long)(x) & ~(PAGE_SIZE-1)) -#define BYTE_ADD(x,n) (void*)((char*)(x) + (n)) -#define BYTE_SUB(x,n) (void*)((char*)(x) - (n)) - -/* This is for global allocation of dentries. Remove this when - * converting to SLAB. - */ -struct dheader { - struct dentry * emptylist; - short free, maxfree; - struct dheader * next; - struct dheader * prev; -}; - -struct anchors { - struct dheader * free; /* each contains at least 1 empty dentry */ - struct dheader * full; /* all the used up ones */ - struct dheader * dir_free; - struct dheader * dir_full; -}; - -/* This is only used for directory dentries. Think of it as an extension - * of the dentry. - * It is defined as separate struct, so it uses up space only - * where necessary. +/* + * This is the single most critical data structure when it comes + * to the dcache: the hashtable for lookups. Somebody should try + * to make this good - I've just made it work. + * + * This hash-function tries to avoid losing too many bits of hash + * information, yet avoid using a prime hash-size or similar. */ -struct ddir { - struct dentry * dd_hashtable[D_HASHSIZE]; - struct dentry * dd_neglist; - struct dentry * dd_basketlist; - struct dentry * dd_zombielist; - unsigned short dd_alloced; /* # d_alloc()ed, but not yet d_add()ed */ - unsigned short dd_hashed; /* # of entries in hashtable */ - unsigned short dd_true_hashed; /* # non-preliminaries in hashtable */ - unsigned short dd_negs; /* # of negative entries */ -}; - -DEF_INSERT(header,struct dheader,next,prev) -DEF_REMOVE(header,struct dheader,next,prev) +#define D_HASHBITS 10 +#define D_HASHSIZE (1UL << D_HASHBITS) +#define D_HASHMASK (D_HASHSIZE-1) -DEF_INSERT(alias,struct dentry,d_next,d_prev) -DEF_REMOVE(alias,struct dentry,d_next,d_prev) +static struct list_head dentry_hashtable[D_HASHSIZE]; +static LIST_HEAD(dentry_unused); -DEF_INSERT(hash,struct dentry,d_hash_next,d_hash_prev) -DEF_REMOVE(hash,struct dentry,d_hash_next,d_hash_prev) - -DEF_INSERT(basket,struct dentry,d_basket_next,d_basket_prev) -DEF_REMOVE(basket,struct dentry,d_basket_next,d_basket_prev) - -static struct anchors anchors[4]; - -struct dentry * the_root = NULL; - -unsigned long name_cache_init(unsigned long mem_start, unsigned long mem_end) +void d_free(struct dentry *dentry) { - memset(anchors, 0, sizeof(anchors)); - return mem_start; + kfree(dentry->d_name.name); + kfree(dentry); } -#ifdef DEBUG -/* throw this away after the beta phase */ -/*************************************************************************/ -extern void xcheck(char * txt, struct inode * p); - -static int x_alloc = 0; -static int x_freed = 0; -static int x_free = 0; - -static void * tst[20000]; -static int cnt = 0; - -static void ins(void* ptr) -{ - extern int inodes_stat; - tst[cnt++] = ptr; - if(cnt % 1000 == 0) - printk("------%d allocated: %d: %d %d %d\n", inodes_stat, cnt, - x_alloc, x_freed, x_free); - if(cnt>=20000) panic("stop"); -} - -#if 0 -static inline int search(void* ptr) -{ - int i; - for(i = cnt-1; i>=0; i--) - if(tst[i] == ptr) - return i; - return -1; -} - -#define TST(n,x) if(search(x)<0) printk("%s bad ptr %p line %d\n", n, x, __LINE__) -#else -#define TST(n,x) /*nothing*/ -#endif - -void LOG(char * txt, struct dentry * entry) -{ - static int count = 0; - if(entry) { - TST(txt,entry); - } - if(count) { - count--; - printk("%s: entry=%p\n", txt, entry); - } -} - -#ifdef DEBUG_DDIR_COUNT -static struct ddir * d_dir(struct dentry * entry); -void recursive_test(struct dentry * entry) -{ - int i; - struct ddir * ddir = d_dir(entry); - int sons = 0; - - if(ddir->dd_zombielist) - sons++; - for(i=0; i < D_HASHSIZE; i++) { - struct dentry ** base = &ddir->dd_hashtable[i]; - struct dentry * tmp = *base; - if(tmp) do { - TST("__clear",tmp); - if(!(tmp->d_flag & D_HASHED)) { - printk("VFS: dcache entry not hashed!\n"); - printpath(*base); printk("\n"); - printpath(tmp); - } - if(!(tmp->d_flag & D_PRELIMINARY)) - sons++; - if(tmp->d_flag & D_DIR) - recursive_test(tmp); - tmp = tmp->d_hash_next; - } while(tmp && tmp != *base); - } - if(!sons && !(entry->d_flag & D_PRELIMINARY) && entry->u.d_inode) { - struct inode * inode = entry->u.d_inode; - if(!atomic_read(&inode->i_count)) { - if(!(inode->i_status & 1/*ST_AGED*/)) { - printpath(entry); - printk(" is not aged!\n"); - } - if(inode->i_ddir_count) { - printpath(entry); - printk(" has ddir_count blockage!\n"); +/* + * dput() + * + * This is complicated by the fact that we do not want to put + * dentries that are no longer on any hash chain on the unused + * list: we'd much rather just get rid of them immediately. + * + * However, that implies that we have to traverse the dentry + * tree upwards to the parents which might _also_ now be + * scheduled for deletion (it may have been only waiting for + * its last child to go away). + * + * This tail recursion is done by hand as we don't want to depend + * on the compiler to always get this right (gcc generally doesn't). + * Real recursion would eat up our stack space. + */ +void dput(struct dentry *dentry) +{ + if (dentry) { + int count; +repeat: + count = dentry->d_count-1; + if (count < 0) { + printk("Negative d_count (%d) for %s/%s\n", + count, + dentry->d_parent->d_name.name, + dentry->d_name.name); + *(int *)0 = 0; + } + dentry->d_count = count; + if (!count) { + list_del(&dentry->d_lru); + if (list_empty(&dentry->d_hash)) { + struct inode *inode = dentry->d_inode; + struct dentry * parent; + if (inode) { + list_del(&dentry->d_alias); + iput(inode); + } + parent = dentry->d_parent; + d_free(dentry); + if (dentry == parent) + return; + dentry = parent; + goto repeat; } + list_add(&dentry->d_lru, &dentry_unused); } } } -#else -#define recursive_test(e) /*nothing*/ -#endif -#else -#define TST(n,x) /*nothing*/ -#define LOG(n,x) /*nothing*/ -#define xcheck(t,i) /*nothing*/ -#define recursive_test(e) /*nothing*/ -/*****************************************************************************/ -#endif -void printpath(struct dentry * entry) -{ - if(!IS_ROOT(entry)) - printpath(entry->d_parent); - printk("/%s", entry->d_name); -} - -static inline long has_sons(struct ddir * ddir) -{ - return ((ddir->dd_alloced | ddir->dd_hashed) || - ddir->dd_neglist || - ddir->dd_basketlist || - ddir->dd_zombielist); -} - -static inline int has_true_sons(struct ddir * ddir) -{ - return (ddir->dd_alloced | ddir->dd_true_hashed); -} - -/* Only hold the i_ddir_count pseudo refcount when neccessary (i.e. when - * they have true_sons), to prevent keeping too much dir inodes in use. +/* + * Shrink the dcache. This is done when we need + * more memory, or simply when we need to unmount + * something (at which point we need to unuse + * all dentries). */ -static inline void inc_ddir(struct dentry * entry, struct inode * inode) -{ - if(!(entry->d_flag & D_INC_DDIR)) { - entry->d_flag |= D_INC_DDIR; -#ifdef DEBUG - if(inode->i_ddir_count) { - printpath(entry); - printk(" ddir_count=%d\n", inode->i_ddir_count); +void shrink_dcache(void) +{ + for (;;) { + struct dentry *dentry; + struct list_head *tmp = dentry_unused.prev; + + if (tmp == &dentry_unused) + break; + list_del(tmp); + INIT_LIST_HEAD(tmp); + dentry = list_entry(tmp, struct dentry, d_lru); + if (!dentry->d_count) { + struct dentry * parent; + + list_del(&dentry->d_hash); + if (dentry->d_inode) { + struct inode * inode = dentry->d_inode; + + list_del(&dentry->d_alias); + dentry->d_inode = NULL; + iput(inode); + } + parent = dentry->d_parent; + d_free(dentry); + dput(parent); } -#endif - inode->i_ddir_count++; - _get_inode(inode); - } -} - -static inline blocking void dec_ddir(struct dentry * entry, struct inode * inode) -{ - if(entry->d_flag & D_INC_DDIR) { - entry->d_flag &= ~D_INC_DDIR; - inode->i_ddir_count--; - if(!inode->i_ddir_count) - __iput(inode); } } -/* Do not inline this many times. */ -static void d_panic(void) -{ - panic("VFS: dcache directory corruption"); -} +#define NAME_ALLOC_LEN(len) ((len+16) & ~15) -static inline struct ddir * d_dir(struct dentry * entry) +struct dentry * d_alloc(struct dentry * parent, const struct qstr *name) { - struct ddir * res = BYTE_SUB(entry, sizeof(struct ddir)); - - if(!(entry->d_flag & D_DIR)) - d_panic(); -#ifdef DEBUG - if(!entry) - panic("entry NULL!"); - if(BASE_DHEADER(res) != BASE_DHEADER(entry)) - printk("Scheisse!!!\n"); -#endif - return res; -} + char * str; + struct dentry *dentry; -static /*inline*/ struct dheader * dinit(int isdir, int size) -{ - struct dheader * res = (struct dheader*)__get_free_page(GFP_KERNEL); - int restlen = PAGE_SIZE - sizeof(struct dheader); - struct dentry * ptr = BYTE_ADD(res, sizeof(struct dheader)); + dentry = kmalloc(sizeof(struct dentry), GFP_KERNEL); + if (!dentry) + return NULL; - if(!res) + str = kmalloc(NAME_ALLOC_LEN(name->len), GFP_KERNEL); + if (!str) { + kfree(dentry); return NULL; - memset(res, 0, sizeof(struct dheader)); - if(isdir) { - ptr = BYTE_ADD(ptr, sizeof(struct ddir)); - size += sizeof(struct ddir); } - if(BASE_DHEADER(ptr) != res) - panic("Bad kernel page alignment"); - size += sizeof(struct dentry) - D_MAXLEN; - res->emptylist = NULL; - res->free = 0; - while(restlen >= size) { -#ifdef DEBUG - ins(ptr); - if(BASE_DHEADER(ptr) != res) - panic("Wrong dinit!"); -#endif - ptr->d_next = res->emptylist; - res->emptylist = ptr; - ptr = BYTE_ADD(ptr, size); - res->free++; - restlen -= size; - } - res->maxfree = res->free; - return res; -} -static /*inline*/ struct dentry * __dalloc(struct anchors * anchor, - struct dentry * parent, int isdir, - int len, int size) -{ - struct dheader ** free = isdir ? &anchor->dir_free : &anchor->free; - struct dheader ** full = isdir ? &anchor->dir_full : &anchor->full; - struct dheader * base = *free; - struct dentry * res; + memcpy(str, name->name, name->len); + str[name->len] = 0; - if(!base) { - base = dinit(isdir, size); - if(!base) - return NULL; - insert_header(free, base); - } - base->free--; - res = base->emptylist; - if(!(base->emptylist = res->d_next)) { - remove_header(free, base); - insert_header(full, base); - } - memset(res, 0, sizeof(struct dentry) - D_MAXLEN); - if(isdir) { - res->d_flag = D_DIR; - memset(d_dir(res), 0, sizeof(struct ddir)); - } - res->d_len = len; - res->d_parent = parent; - if(parent) { - struct ddir * pdir = d_dir(parent); -#ifdef DEBUG - if(pdir->dd_alloced > 1 && !IS_ROOT(parent)) { - printpath(parent); - printk(" dd_alloced=%d\n", pdir->dd_alloced); - } -#endif - pdir->dd_alloced++; - } -#ifdef DEBUG - x_alloc++; -#endif - return res; + dentry->d_count = 0; + dentry->d_flags = 0; + dentry->d_inode = NULL; + dentry->d_parent = parent; + dentry->d_mounts = dentry; + dentry->d_covers = dentry; + INIT_LIST_HEAD(&dentry->d_hash); + INIT_LIST_HEAD(&dentry->d_alias); + INIT_LIST_HEAD(&dentry->d_lru); + + dentry->d_name.name = str; + dentry->d_name.len = name->len; + dentry->d_name.hash = name->hash; + dentry->d_revalidate = NULL; + return dentry; } -struct dentry * d_alloc(struct dentry * parent, int len, int isdir) +/* + * Fill in inode information in the entry. + * + * This turns negative dentries into productive full members + * of society. + * + * NOTE! This assumes that the inode count has been incremented + * (or otherwise set) by the caller to indicate that it is now + * in use by the dcache.. + */ +void d_instantiate(struct dentry *entry, struct inode * inode) { - int i, size; + if (inode) + list_add(&entry->d_alias, &inode->i_dentry); -#ifdef DEBUG - if(the_root) - recursive_test(the_root); - LOG("d_alloc", parent); -#endif - if(len >= D_MEDIUM) { - if(len >= D_LARGE) { - i = 3; - size = D_HUGE; - } else { - i = 2; - size = D_LARGE; - } - } else if(len >= D_SMALL) { - i = 1; - size = D_MEDIUM; - } else { - i = 0; - size = D_SMALL; - } - return __dalloc(&anchors[i], parent, isdir, len, size); + entry->d_inode = inode; } -extern blocking struct dentry * d_alloc_root(struct inode * root_inode) +struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root) { - struct dentry * res = the_root; - - if(res) { - d_del(res, D_NO_CLEAR_INODE); /* invalidate everything beyond */ - } else { - struct ddir * ddir; + struct dentry *res = NULL; - the_root = res = d_alloc(NULL, 0, 1); - LOG("d_alloc_root", res); + if (root_inode) { + res = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 }); res->d_parent = res; - res->d_name[0]='\0'; - ddir = d_dir(res); - ddir->dd_alloced = 999; /* protect from deletion */ + res->d_count = 1; + d_instantiate(res, root_inode); } - insert_alias(&root_inode->i_dentry, res); - root_inode->i_dent_count++; - root_inode->i_ddir_count++; - res->u.d_inode = root_inode; return res; } -static inline unsigned long d_hash(char first, char last) +static inline struct list_head * d_hash(struct dentry * parent, unsigned long hash) { - return ((unsigned long)first ^ ((unsigned long)last << 4)) & (D_HASHSIZE-1); + hash += (unsigned long) parent; + hash = hash ^ (hash >> D_HASHBITS) ^ (hash >> D_HASHBITS*2); + return dentry_hashtable + (hash & D_HASHMASK); } -static inline struct dentry ** d_base_entry(struct ddir * pdir, struct dentry * entry) +static inline struct dentry * __dlookup(struct list_head *head, struct dentry * parent, struct qstr * name) { - return &pdir->dd_hashtable[d_hash(entry->d_name[0], - entry->d_name[entry->d_len-1])]; -} + struct list_head *tmp = head->next; + int len = name->len; + int hash = name->hash; + const unsigned char *str = name->name; -static inline struct dentry ** d_base_qstr(struct ddir * pdir, - struct qstr * s1, - struct qstr * s2) -{ - unsigned long hash; + while (tmp != head) { + struct dentry * dentry = list_entry(tmp, struct dentry, d_hash); - if(s2 && s2->len) { - hash = d_hash(s1->name[0], s2->name[s2->len-1]); - } else { - hash = d_hash(s1->name[0], s1->name[s1->len-1]); + tmp = tmp->next; + if (dentry->d_name.hash != hash) + continue; + if (dentry->d_name.len != len) + continue; + if (dentry->d_parent != parent) + continue; + if (memcmp(dentry->d_name.name, str, len)) + continue; + return dentry; } - return &pdir->dd_hashtable[hash]; -} - - -static /*inline*/ blocking void _d_remove_from_parent(struct dentry * entry, - struct ddir * pdir, - struct inode * inode, - int flags) -{ - if(entry->d_flag & D_HASHED) { - struct dentry ** base = d_base_entry(pdir, entry); - - remove_hash(base, entry); - entry->d_flag &= ~D_HASHED; - pdir->dd_hashed--; - if(!(entry->d_flag & D_PRELIMINARY)) { - pdir->dd_true_hashed--; - if(!inode) { -#ifdef DEBUG - if(!entry->d_next || !entry->d_prev) { - printpath(entry); - printk(" flags=%x d_flag=%x negs=%d " - "hashed=%d\n", flags, entry->d_flag, - pdir->dd_negs, pdir->dd_hashed); - } -#endif - remove_alias(&pdir->dd_neglist, entry); - pdir->dd_negs--; - } - } - } else if(!(entry->d_flag & D_ZOMBIE)) { -#ifdef DEBUG - if(!pdir->dd_alloced) printk("dd_alloced is 0!\n"); -#endif - pdir->dd_alloced--; - } - if(entry->d_flag & D_BASKET) { - remove_basket(&pdir->dd_basketlist, entry); - entry->d_flag &= ~D_BASKET; - } -} - -/* Theoretically, zombies should never or extremely seldom appear, - * so this code is nearly superfluous. - * A way to get zombies is while using inodes (i_count>0), unlink() - * them as well as rmdir() the parent dir => the parent dir becomes a zombie. - * Zombies are *not* in the hashtable, because somebody could re-creat() - * that filename in it's parent dir again. - * Besides coding errors during beta phase, when forcing an umount() - * (e.g. at shutdown time), inodes could be in use such that the parent - * dir is cleared, resulting also in zombies. - */ -static /*inline*/ void _d_handle_zombie(struct dentry * entry, - struct ddir * ddir, - struct ddir * pdir) -{ - if(entry->d_flag & D_DIR) { - if(entry->d_flag & D_ZOMBIE) { - if(!has_sons(ddir)) { - entry->d_flag &= ~D_ZOMBIE; - remove_hash(&pdir->dd_zombielist, entry); - if(!pdir->dd_zombielist && - (entry->d_parent->d_flag & D_ZOMBIE)) { - d_del(entry->d_parent, D_NORMAL); - } - } - } else if(has_sons(ddir)) { - entry->d_flag |= D_ZOMBIE; - insert_hash(&pdir->dd_zombielist, entry); - - /* This condition is no longer a bug, with the removal - * of recursive_clear() this happens naturally during - * an unmount attempt of a filesystem which is busy. - */ -#if 0 - /* Not sure when this message should show up... */ - if(!IS_ROOT(entry)) { - printk("VFS: clearing dcache directory " - "with successors\n"); -#ifdef DEBUG - printpath(entry); - printk(" d_flag=%x alloced=%d negs=%d hashed=%d " - "basket=%p zombies=%p\n", - entry->d_flag, ddir->dd_alloced, - ddir->dd_negs, ddir->dd_hashed, - ddir->dd_basketlist, ddir->dd_zombielist); -#endif - } -#endif - } - } -} - -static /*inline*/ blocking void _d_del(struct dentry * entry, - struct anchors * anchor, - int flags) -{ - struct dheader ** free; - struct dheader ** full; - struct dheader * base = BASE_DHEADER(entry); - struct ddir * ddir = NULL; - struct ddir * pdir; - struct inode * inode = entry->d_flag & D_PRELIMINARY ? NULL : entry->u.d_inode; - -#ifdef DEBUG - if(inode) - xcheck("_d_del", inode); -#endif - if(!entry->d_parent) { - printk("VFS: dcache parent is NULL\n"); - return; - } - if(entry->d_flag & D_DIR) { - free = &anchor->dir_free; - full = &anchor->dir_full; - } else { - free = &anchor->free; - full = &anchor->full; - } - pdir = d_dir(entry->d_parent); - if(!IS_ROOT(entry)) - _d_remove_from_parent(entry, pdir, inode, flags); - - /* This may block, be careful! _d_remove_from_parent() is - * thus called before. - */ - if(entry->d_flag & D_DIR) - ddir = d_dir(entry); - if(IS_ROOT(entry)) - return; - - if(flags & D_NO_FREE) { - /* Make it re-d_add()able */ - pdir->dd_alloced++; - entry->d_flag &= D_DIR; - } else - _d_handle_zombie(entry, ddir, pdir); - - /* This dec_ddir() must occur after zombie handling. */ - if(!has_true_sons(pdir)) - dec_ddir(entry->d_parent, entry->d_parent->u.d_inode); - - entry->u.d_inode = NULL; - if(inode) { - remove_alias(&inode->i_dentry, entry); - inode->i_dent_count--; - if (entry->d_flag & D_DIR) - dec_ddir(entry, inode); - - if(!(flags & D_NO_CLEAR_INODE) && - !(atomic_read(&inode->i_count) + - inode->i_ddir_count + - inode->i_dent_count)) { -#ifdef DEBUG - printk("#"); -#endif - /* This may block also. */ - _clear_inode(inode, 0, 0); - } - } - if(!(flags & D_NO_FREE) && !(entry->d_flag & D_ZOMBIE)) { - base->free++; - if(base->free == base->maxfree) { -#ifndef DEBUG - remove_header(free, base); - free_page((unsigned long)base); - goto done; -#endif - } - entry->d_next = base->emptylist; - base->emptylist = entry; - if(!entry->d_next) { - remove_header(full, base); - insert_header(free, base); - } -#ifdef DEBUG - x_freed++; -#endif - } -#ifndef DEBUG -done: -#else - x_free++; -#endif + return NULL; } -blocking void d_del(struct dentry * entry, int flags) +struct dentry * d_lookup(struct dentry * dir, struct qstr * name) { - int i; - - if(!entry) - return; - LOG("d_clear", entry); - if(entry->d_len >= D_MEDIUM) { - if(entry->d_len >= D_LARGE) { - i = 3; - } else { - i = 2; - } - } else if(entry->d_len >= D_SMALL) { - i = 1; - } else { - i = 0; - } - _d_del(entry, &anchors[i], flags); + return __dlookup(d_hash(dir, name->hash), dir, name); } -static inline struct dentry * __dlookup(struct dentry ** base, - struct qstr * name, - struct qstr * appendix) +static inline void d_insert_to_parent(struct dentry * entry, struct dentry * parent) { - struct dentry * tmp = *base; - - if(tmp && name->len) { - int totallen = name->len; - - if(appendix) - totallen += appendix->len; - do { - if(tmp->d_len == totallen && - !(tmp->d_flag & D_DUPLICATE) && - !strncmp(tmp->d_name, name->name, name->len) && - (!appendix || !strncmp(tmp->d_name+name->len, - appendix->name, appendix->len))) - return tmp; - tmp = tmp->d_hash_next; - } while(tmp != *base); - } - return NULL; + list_add(&entry->d_hash, d_hash(dget(parent), entry->d_name.hash)); } -struct dentry * d_lookup(struct inode * dir, - struct qstr * name, - struct qstr * appendix) +static inline void d_remove_from_parent(struct dentry * dentry, struct dentry * parent) { - if(dir->i_dentry) { - struct ddir * ddir = d_dir(dir->i_dentry); - struct dentry ** base = d_base_qstr(ddir, name, appendix); - - return __dlookup(base, name, appendix); - } - return NULL; + list_del(&dentry->d_hash); + dput(parent); } -static /*inline*/ blocking void _d_insert_to_parent(struct dentry * entry, - struct ddir * pdir, - struct inode * inode, - struct qstr * ininame, - int flags) -{ - struct dentry ** base; - struct dentry * parent = entry->d_parent; -#ifdef DEBUG - if(!pdir->dd_alloced) - printk("dd_alloced is 0!\n"); -#endif - base = d_base_qstr(pdir, ininame, NULL); - if(!(flags & (D_NOCHECKDUP|D_DUPLICATE)) && - __dlookup(base, ininame, NULL)) { - d_del(entry, D_NO_CLEAR_INODE); - return; - } - if(entry->d_flag & D_HASHED) { - printk("VFS: dcache entry is already hashed\n"); - return; - } - if(!(flags & D_PRELIMINARY)) - pdir->dd_true_hashed++; - pdir->dd_hashed++; - insert_hash(base, entry); - entry->d_flag |= D_HASHED; - pdir->dd_alloced--; - if(flags & D_BASKET) - insert_basket(&pdir->dd_basketlist, entry); - -#ifdef DEBUG - if(inode && inode->i_dentry && (entry->d_flag & D_DIR)) { - struct dentry * tmp = inode->i_dentry; - printk("Auweia inode=%p entry=%p (%p %p %s)\n", - inode, entry, parent->u.d_inode, parent, parent->d_name); - printk("entry path="); printpath(entry); printk("\n"); - do { - TST("auweia",tmp); - printk("alias path="); printpath(tmp); printk("\n"); - tmp = tmp->d_next; - } while(tmp != inode->i_dentry); - printk("\n"); - } -#endif - if(has_true_sons(pdir)) - inc_ddir(parent, parent->u.d_inode); - if(!inode && !(flags & D_PRELIMINARY)) { - insert_alias(&pdir->dd_neglist, entry); - pdir->dd_negs++; - - /* Don't allow the negative list to grow too much ... */ - while(pdir->dd_negs > (pdir->dd_true_hashed >> 1) + 5) - d_del(pdir->dd_neglist->d_prev, D_REMOVE); - } -} - -blocking void d_add(struct dentry * entry, struct inode * inode, - struct qstr * ininame, int flags) +/* + * When a file is deleted, we have two options: + * - turn this dentry into a negative dentry + * - unhash this dentry and free it. + * + * Usually, we want to just turn this into + * a negative dentry, but if anybody else is + * currently using the dentry or the inode + * we can't do that and we fall back on removing + * it from the hash queues and waiting for + * it to be deleted later when it has no users + */ +void d_delete(struct dentry * dentry) { - struct dentry * parent = entry->d_parent; - struct qstr dummy; - struct ddir * pdir; + /* + * Are we the only user? + */ + if (dentry->d_count == 1) { + struct inode * inode = dentry->d_inode; -#ifdef DEBUG - if(inode) - xcheck("d_add", inode); - if(IS_ROOT(entry)) { - printk("VFS: d_add for root dentry "); - printpath(entry); - printk(" -> "); - if(ininame) - printk("%s", ininame->name); - printk("\n"); + dentry->d_inode = NULL; + list_del(&dentry->d_alias); + iput(inode); return; } - if(!parent) - panic("d_add with parent==NULL"); - LOG("d_add", entry); -#endif - if(ininame) { - if(ininame->len != entry->d_len) { - printk("VFS: d_add with wrong string length"); - entry->d_len = ininame->len; /* kludge */ - } - memcpy(entry->d_name, ininame->name, ininame->len); - entry->d_name[ininame->len] = '\0'; - } else { - dummy.name = entry->d_name; - dummy.len = entry->d_len; - ininame = &dummy; - } - if(entry->d_flag & D_HASHED) - printk("VFS: d_add of already added dcache entry\n"); - pdir = d_dir(parent); - _d_insert_to_parent(entry, pdir, inode, ininame, flags); - entry->d_flag |= flags; - if(inode && !(flags & D_PRELIMINARY)) { - if(entry->d_flag & D_DIR) { - if(inode->i_dentry) { - printk("VFS: creating dcache directory alias\n"); - return; - } - } - insert_alias(&inode->i_dentry, entry); - inode->i_dent_count++; - } - entry->u.d_inode = inode; + /* + * If not, just drop the dentry and let dput + * pick up the tab.. + */ + d_drop(dentry); } -blocking struct dentry * d_entry(struct dentry * parent, - struct qstr * name, - struct inode * inode) +void d_add(struct dentry * entry, struct inode * inode) { - struct ddir * pdir = d_dir(parent); - struct dentry ** base = d_base_qstr(pdir, name, NULL); - struct dentry * found = __dlookup(base, name, NULL); - - if(!found) { - int isdir = (inode && S_ISDIR(inode->i_mode)); - - found = d_alloc(parent, name->len, isdir); - if(found) { - d_add(found, inode, name, - isdir ? (D_DIR|D_NOCHECKDUP) : D_NOCHECKDUP); - } else - printk("VFS: problem with d_alloc\n"); - } - return found; + d_insert_to_parent(entry, entry->d_parent); + d_instantiate(entry, inode); } -blocking void d_entry_preliminary(struct dentry * parent, - struct qstr * name, - unsigned long ino) +static inline void alloc_new_name(struct dentry * entry, struct qstr *newname) { - struct ddir * pdir = d_dir(parent); - struct dentry ** base = d_base_qstr(pdir, name, NULL); - struct dentry * found = __dlookup(base, name, NULL); + int len = newname->len; + int hash = newname->hash; + char *name = (char *) entry->d_name.name; - if(!found && ino) { - struct dentry * new = d_alloc(parent, name->len, 0); - - if(new) { - d_add(new, NULL, name, D_PRELIMINARY|D_NOCHECKDUP); - new->u.d_ino = ino; - } else - printk("VFS: problem with d_alloc\n"); + if (NAME_ALLOC_LEN(len) != NAME_ALLOC_LEN(entry->d_name.len)) { + name = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL); + if (!name) + printk("out of memory for dcache\n"); + kfree(entry->d_name.name); + entry->d_name.name = name; } + memcpy(name, newname->name, len); + name[len] = 0; + entry->d_name.len = len; + entry->d_name.hash = hash; } -blocking void d_move(struct dentry * entry, struct inode * newdir, - struct qstr * newname, struct qstr * newapp) +void d_move(struct dentry * dentry, struct dentry * newdir, struct qstr * newname) { - struct ddir tmp; - struct dentry * new; - struct inode * inode; - int len; - int flags; - - if(!entry) + if (!dentry) return; - inode = entry->u.d_inode; - flags = entry->d_flag; - if((flags & D_PRELIMINARY) || !inode) { - if(!(flags & D_PRELIMINARY)) - printk("VFS: trying to move negative dcache entry\n"); - d_del(entry, D_NO_CLEAR_INODE); - return; - } -#if 0 -printk("d_move %p '%s' -> '%s%s' dent_count=%d\n", inode, entry->d_name, - newname->name, newapp ? newapp->name : "", inode->i_dent_count); -#endif - if(flags & D_ZOMBIE) { - printk("VFS: moving zombie entry\n"); - } - if(flags & D_DIR) { - struct ddir * ddir = d_dir(entry); - - memcpy(&tmp, ddir, sizeof(struct ddir)); - /* Simulate empty dir for d_del(). */ - memset(ddir, 0, sizeof(struct ddir)); - } - len = newname->len; - if(newapp) { - len += newapp->len; - flags |= D_BASKET; - } else - flags &= ~D_BASKET; - new = d_alloc(newdir->i_dentry, len, flags & D_DIR); - memcpy(new->d_name, newname->name, newname->len); - if(newapp) - memcpy(new->d_name+newname->len, newapp->name, newapp->len); - new->d_name[len] = '\0'; - d_del(entry, D_NO_CLEAR_INODE); - d_add(new, inode, NULL, flags & (D_DIR|D_BASKET)); - if(flags & D_DIR) { - struct ddir * ddir = d_dir(new); + if (!dentry->d_inode) + printk("VFS: moving negative dcache entry\n"); - memcpy(ddir, &tmp, sizeof(struct ddir)); - } + d_remove_from_parent(dentry, dentry->d_parent); + alloc_new_name(dentry, newname); + dentry->d_parent = newdir; + d_insert_to_parent(dentry, newdir); } -int d_path(struct dentry * entry, struct inode * chroot, char * buf) +int d_path(struct dentry * entry, struct dentry * chroot, char * buf) { - if(IS_ROOT(entry) || (chroot && entry->u.d_inode == chroot && - !(entry->d_flag & D_PRELIMINARY))) { + if (IS_ROOT(entry) || (chroot && entry == chroot)) { *buf = '/'; return 1; } else { int len = d_path(entry->d_parent, chroot, buf); buf += len; - if(len > 1) { + if (len > 1) { *buf++ = '/'; len++; } - memcpy(buf, entry->d_name, entry->d_len); - return len + entry->d_len; + memcpy(buf, entry->d_name.name, entry->d_name.len); + return len + entry->d_name.len; } } -struct dentry * d_basket(struct dentry * dir_entry) +void dcache_init(void) { - if(dir_entry && (dir_entry->d_flag & D_DIR)) { - struct ddir * ddir = d_dir(dir_entry); - - return ddir->dd_basketlist; - } else - return NULL; -} - -int d_isbasket(struct dentry * entry) -{ - return entry->d_flag & D_BASKET; -} - -blocking struct inode * d_inode(struct dentry ** changing_entry) -{ - struct dentry * entry = *changing_entry; - struct inode * inode; - -#ifdef CONFIG_DCACHE_PRELOAD - if(entry->d_flag & D_PRELIMINARY) { - struct qstr name = { entry->d_name, entry->d_len }; - struct ddir * pdir = d_dir(entry->d_parent); - struct dentry ** base = d_base_qstr(pdir, &name, NULL); - struct dentry * found; - unsigned long ino; - struct inode * dir = entry->d_parent->u.d_inode; - TST("d_inode",entry); - ino = entry->u.d_ino; - if(!dir) - d_panic(); - - /* Prevent concurrent d_lookup()s or d_inode()s before - * giving up vfs_lock. This just removes from the parent, - * but does not deallocate it. - */ - - /* !!!!!!! Aiee, here is an unresolved race if somebody - * unlink()s the inode during the iget(). The problem is - * that we need to synchronize externally. Proposed solution: - * put a rw_lock (read-mode) on the parent dir for each - * iget(), lookup() and so on, and a write-mode lock for - * everything that changes the dir (e.g. unlink()), and do - * this consistently everywhere in the generic VFS (not in - * the concrete filesystems). This should kill similar - * races everywhere, with a single clean concept. - * Later, the synchronization stuff can be cleaned out - * of the concrete fs'es. - */ - d_del(entry, D_NO_CLEAR_INODE|D_NO_FREE); - vfs_unlock(); - - /* This circumvents the normal lookup() of pathnames. - * Therefore, preliminary entries must not be used - * (see FS_NO_DCACHE and FS_NO_PRELIM) if the fs does not - * permit fetching *valid* inodes with plain iget(). - */ - inode = __iget(dir->i_sb, ino, 0); - vfs_lock(); - if(!inode) { - printk("VFS: preliminary dcache entry was invalid\n"); - *changing_entry = NULL; - return NULL; - } - xcheck("d_inode iget()", inode); - if((found = __dlookup(base, &name, NULL))) { - d_del(entry, D_NO_CLEAR_INODE); - *changing_entry = found; - } else if(S_ISDIR(inode->i_mode)) { - struct dentry * new = d_alloc(entry->d_parent, entry->d_len, 1); - if(new) - d_add(new, inode, &name, D_DIR); - *changing_entry = new; - - /* Finally deallocate old entry. */ - d_del(entry, D_NO_CLEAR_INODE); - } else { - /* Re-insert to the parent, but now as normal dentry. */ - d_add(entry, inode, NULL, 0); - } - return inode; - } -#endif - inode = entry->u.d_inode; - if(inode) { -#ifdef DEBUG - xcheck("d_inode", inode); -#endif - iinc_zero(inode); - } - return inode; + int i; + struct list_head *d = dentry_hashtable; + + i = D_HASHSIZE; + do { + INIT_LIST_HEAD(d); + d++; + i--; + } while (i); } diff --git a/fs/devices.c b/fs/devices.c index d3b1d6846..26f668e7f 100644 --- a/fs/devices.c +++ b/fs/devices.c @@ -209,9 +209,7 @@ int check_disk_change(kdev_t dev) printk(KERN_DEBUG "VFS: Disk change detected on device %s\n", kdevname(dev)); - for (i=0 ; i<NR_SUPER ; i++) - if (super_blocks[i].s_dev == dev) - put_super(super_blocks[i].s_dev); + invalidate_inodes(dev); invalidate_buffers(dev); diff --git a/fs/dquot.c b/fs/dquot.c index 59d2112d9..b19c939be 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -13,7 +13,7 @@ * diskquota system. This implementation is not based on any BSD * kernel sourcecode. * - * Version: $Id: dquot.c,v 1.11 1997/01/06 06:53:02 davem Exp $ + * Version: $Id: dquot.c,v 1.2 1997/06/17 13:25:58 ralf Exp $ * * Author: Marco van Wieringen <mvw@mcs.ow.nl> <mvw@tnix.net> * @@ -227,7 +227,7 @@ static void write_dquot(struct dquot *dquot) lock_dquot(dquot); down(&dquot->dq_mnt->mnt_sem); if (filp->f_op->llseek) { - if (filp->f_op->llseek(filp->f_inode, filp, + if (filp->f_op->llseek(filp->f_dentry->d_inode, filp, dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) { up(&dquot->dq_mnt->mnt_sem); unlock_dquot(dquot); @@ -238,10 +238,9 @@ static void write_dquot(struct dquot *dquot) fs = get_fs(); set_fs(KERNEL_DS); - if (filp->f_op->write(filp->f_inode, filp, + if (filp->f_op->write(filp->f_dentry->d_inode, filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk)) == sizeof(struct dqblk)) dquot->dq_flags &= ~DQ_MOD; - /* inode->i_status |= ST_MODIFIED is willingly *not* done here */ up(&dquot->dq_mnt->mnt_sem); set_fs(fs); @@ -260,7 +259,7 @@ static void read_dquot(struct dquot *dquot) lock_dquot(dquot); down(&dquot->dq_mnt->mnt_sem); if (filp->f_op->llseek) { - if (filp->f_op->llseek(filp->f_inode, filp, + if (filp->f_op->llseek(filp->f_dentry->d_inode, filp, dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) { up(&dquot->dq_mnt->mnt_sem); unlock_dquot(dquot); @@ -270,7 +269,7 @@ static void read_dquot(struct dquot *dquot) filp->f_pos = dqoff(dquot->dq_id); fs = get_fs(); set_fs(KERNEL_DS); - filp->f_op->read(filp->f_inode, filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk)); + filp->f_op->read(filp->f_dentry->d_inode, filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk)); up(&dquot->dq_mnt->mnt_sem); set_fs(fs); if (dquot->dq_bhardlimit == 0 && dquot->dq_bsoftlimit == 0 && @@ -947,39 +946,53 @@ int quota_off(kdev_t dev, short type) int quota_on(kdev_t dev, short type, char *path) { - struct file *filp = (struct file *)NULL; + struct file *filp = NULL; + struct dentry *dentry; struct vfsmount *vfsmnt; struct inode *inode; struct dquot *dquot; char *tmp; int error; - if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL) - return(-ENODEV); - if (vfsmnt->mnt_quotas[type] != (struct file *)NULL) - return(-EBUSY); - if ((error = getname(path, &tmp)) != 0) - return(error); - error = open_namei(tmp, O_RDWR, 0600, &inode, 0); + vfsmnt = lookup_vfsmnt(dev); + if (vfsmnt == NULL) + return -ENODEV; + + if (vfsmnt->mnt_quotas[type] != NULL) + return -EBUSY; + + tmp = getname(path); + error = PTR_ERR(tmp); + if (IS_ERR(tmp)) + return error; + + dentry = open_namei(tmp, O_RDWR, 0600); putname(tmp); - if (error) - return(error); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + return error; + inode = dentry->d_inode; + if (!S_ISREG(inode->i_mode)) { - iput(inode); - return(-EACCES); + dput(dentry); + return -EACCES; } - if ((filp = get_empty_filp()) != (struct file *)NULL) { + + filp = get_empty_filp(); + if (filp != NULL) { filp->f_mode = (O_RDWR + 1) & O_ACCMODE; filp->f_flags = O_RDWR; - filp->f_inode = inode; + filp->f_dentry = dentry; filp->f_pos = 0; filp->f_reada = 0; filp->f_op = inode->i_op->default_file_ops; if (filp->f_op->read || filp->f_op->write) { - if ((error = get_write_access(inode)) == 0) { + error = get_write_access(inode); + if (!error) { if (filp->f_op && filp->f_op->open) error = filp->f_op->open(inode, filp); - if (error == 0) { + if (!error) { vfsmnt->mnt_quotas[type] = filp; dquot = dqget(dev, 0, type); vfsmnt->mnt_iexp[type] = (dquot) ? dquot->dq_itime : MAX_IQ_TIME; @@ -987,7 +1000,7 @@ int quota_on(kdev_t dev, short type, char *path) dqput(dquot); vfsmnt->mnt_sb->dq_op = &dquot_operations; add_dquot_ref(dev, type); - return(0); + return 0; } put_write_access(inode); } @@ -996,8 +1009,8 @@ int quota_on(kdev_t dev, short type, char *path) put_filp(filp); } else error = -EMFILE; - iput(inode); - return(error); + dput(dentry); + return error; } /* @@ -1009,7 +1022,6 @@ int quota_on(kdev_t dev, short type, char *path) asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr) { int cmds = 0, type = 0, flags = 0; - struct inode *ino; kdev_t dev; int ret = -EINVAL; @@ -1035,19 +1047,22 @@ asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr) } ret = -EINVAL; - if (special == (char *)NULL && (cmds == Q_SYNC || cmds == Q_GETSTATS)) - dev = 0; - else { - int error = namei(NAM_FOLLOW_LINK, special, &ino); - if(error) + dev = 0; + if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) { + mode_t mode; + struct dentry * dentry; + + dentry = namei(special); + if (IS_ERR(dentry)) goto out; - dev = ino->i_rdev; + + dev = dentry->d_inode->i_rdev; + mode = dentry->d_inode->i_mode; + dput(dentry); + ret = -ENOTBLK; - if (!S_ISBLK(ino->i_mode)) { - iput(ino); + if (!S_ISBLK(mode)) goto out; - } - iput(ino); } ret = -EINVAL; @@ -88,6 +88,10 @@ __initfunc(void binfmt_setup(void)) init_aout_binfmt(); #endif +#ifdef CONFIG_BINFMT_AOUT32 + init_aout32_binfmt(); +#endif + #ifdef CONFIG_BINFMT_JAVA init_java_binfmt(); #endif @@ -134,22 +138,24 @@ int unregister_binfmt(struct linux_binfmt * fmt) } #endif /* CONFIG_MODULES */ -int open_inode(struct inode * inode, int mode) +int open_dentry(struct dentry * dentry, int mode) { int fd; + struct inode * inode = dentry->d_inode; if (!inode->i_op || !inode->i_op->default_file_ops) return -EINVAL; fd = get_unused_fd(); if (fd >= 0) { struct file * f = get_empty_filp(); + if (!f) { put_unused_fd(fd); return -ENFILE; } f->f_flags = mode; f->f_mode = (mode+1) & O_ACCMODE; - f->f_inode = inode; + f->f_dentry = dentry; f->f_pos = 0; f->f_reada = 0; f->f_op = inode->i_op->default_file_ops; @@ -162,7 +168,7 @@ int open_inode(struct inode * inode, int mode) } } current->files->fd[fd] = f; - atomic_inc(&inode->i_count); + dget(dentry); } return fd; } @@ -186,7 +192,7 @@ asmlinkage int sys_uselib(const char * library) goto out; file = current->files->fd[fd]; retval = -ENOEXEC; - if (file && file->f_inode && file->f_op && file->f_op->read) { + if (file && file->f_dentry && file->f_op && file->f_op->read) { for (fmt = formats ; fmt ; fmt = fmt->next) { int (*fn)(int) = fmt->load_shlib; if (!fn) @@ -320,7 +326,7 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm) mpnt->vm_flags = VM_STACK_FLAGS; mpnt->vm_ops = NULL; mpnt->vm_offset = 0; - mpnt->vm_inode = NULL; + mpnt->vm_dentry = NULL; mpnt->vm_pte = 0; insert_vm_struct(current->mm, mpnt); current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; @@ -341,25 +347,18 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm) * that aren't on a block boundary, and for files on filesystems * without bmap support. */ -int read_exec(struct inode *inode, unsigned long offset, +int read_exec(struct dentry *dentry, unsigned long offset, char * addr, unsigned long count, int to_kmem) { struct file file; + struct inode * inode = dentry->d_inode; int result = -ENOEXEC; if (!inode->i_op || !inode->i_op->default_file_ops) goto end_readexec; - file.f_mode = 1; - file.f_flags = 0; - file.f_count = 1; - file.f_inode = inode; - file.f_pos = 0; - file.f_reada = 0; - file.f_op = inode->i_op->default_file_ops; - if (file.f_op->open) - if (file.f_op->open(inode,&file)) - goto end_readexec; - if (!file.f_op || !file.f_op->read) + if (init_private_file(&file, dentry, 1)) + goto end_readexec; + if (!file.f_op->read) goto close_readexec; if (file.f_op->llseek) { if (file.f_op->llseek(inode,&file,offset,0) != offset) @@ -481,7 +480,7 @@ void flush_old_exec(struct linux_binprm * bprm) flush_thread(); if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || - permission(bprm->inode,MAY_READ)) + permission(bprm->dentry->d_inode,MAY_READ)) current->dumpable = 0; flush_old_signals(current->sig); @@ -496,20 +495,21 @@ int prepare_binprm(struct linux_binprm *bprm) { int mode; int retval,id_change; + struct inode * inode = bprm->dentry->d_inode; - mode = bprm->inode->i_mode; + mode = inode->i_mode; if (!S_ISREG(mode)) /* must be regular file */ return -EACCES; if (!(mode & 0111)) /* with at least _one_ execute bit set */ return -EACCES; - if (IS_NOEXEC(bprm->inode)) /* FS mustn't be mounted noexec */ + if (IS_NOEXEC(inode)) /* FS mustn't be mounted noexec */ return -EACCES; - if (!bprm->inode->i_sb) + if (!inode->i_sb) return -EACCES; - if ((retval = permission(bprm->inode, MAY_EXEC)) != 0) + if ((retval = permission(inode, MAY_EXEC)) != 0) return retval; /* better not execute files which are being written to */ - if (bprm->inode->i_writecount > 0) + if (inode->i_writecount > 0) return -ETXTBSY; bprm->e_uid = current->euid; @@ -518,7 +518,7 @@ int prepare_binprm(struct linux_binprm *bprm) /* Set-uid? */ if (mode & S_ISUID) { - bprm->e_uid = bprm->inode->i_uid; + bprm->e_uid = inode->i_uid; if (bprm->e_uid != current->euid) id_change = 1; } @@ -530,7 +530,7 @@ int prepare_binprm(struct linux_binprm *bprm) * executable. */ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { - bprm->e_gid = bprm->inode->i_gid; + bprm->e_gid = inode->i_gid; if (!in_group_p(bprm->e_gid)) id_change = 1; } @@ -539,7 +539,7 @@ int prepare_binprm(struct linux_binprm *bprm) /* We can't suid-execute if we're sharing parts of the executable */ /* or if we're being traced (or if suid execs are not allowed) */ /* (current->mm->count > 1 is ok, as we'll get a new mm anyway) */ - if (IS_NOSUID(bprm->inode) + if (IS_NOSUID(inode) || (current->flags & PF_PTRACED) || (current->fs->count > 1) || (atomic_read(¤t->sig->count) > 1) @@ -550,7 +550,7 @@ int prepare_binprm(struct linux_binprm *bprm) } memset(bprm->buf,0,sizeof(bprm->buf)); - return read_exec(bprm->inode,0,bprm->buf,128,1); + return read_exec(bprm->dentry,0,bprm->buf,128,1); } void remove_arg_zero(struct linux_binprm *bprm) @@ -585,16 +585,19 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) (eh->fh.f_flags & 0x3000) == 0x3000) { char * dynloader[] = { "/sbin/loader" }; - iput(bprm->inode); - bprm->dont_iput = 1; + struct dentry * dentry; + + dput(bprm->dentry); + bprm->dentry = NULL; remove_arg_zero(bprm); bprm->p = copy_strings(1, dynloader, bprm->page, bprm->p, 2); bprm->argc++; bprm->loader = bprm->p; - retval = open_namei(dynloader[0], 0, 0, &bprm->inode, NULL); - if (retval) + dentry = open_namei(dynloader[0], 0, 0); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) return retval; - bprm->dont_iput = 0; + bprm->dentry = dentry; retval = prepare_binprm(bprm); if (retval<0) return retval; @@ -610,15 +613,15 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) continue; retval = fn(bprm, regs); if (retval >= 0) { - if(!bprm->dont_iput) - iput(bprm->inode); - bprm->dont_iput=1; + if (bprm->dentry) + dput(bprm->dentry); + bprm->dentry = NULL; current->did_exec = 1; return retval; } if (retval != -ENOEXEC) break; - if (bprm->dont_iput) /* We don't have the inode anymore*/ + if (!bprm->dentry) /* We don't have the dentry anymore */ return retval; } if (retval != -ENOEXEC) { @@ -647,29 +650,38 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs) { struct linux_binprm bprm; + struct dentry * dentry; int retval; int i; bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */ bprm.page[i] = 0; - retval = open_namei(filename, 0, 0, &bprm.inode, NULL); - if (retval) + + dentry = open_namei(filename, 0, 0); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) return retval; + + bprm.dentry = dentry; bprm.filename = filename; bprm.sh_bang = 0; bprm.java = 0; bprm.loader = 0; bprm.exec = 0; - bprm.dont_iput = 0; - if ((bprm.argc = count(argv)) < 0) + if ((bprm.argc = count(argv)) < 0) { + dput(dentry); return bprm.argc; - if ((bprm.envc = count(envp)) < 0) + } + + if ((bprm.envc = count(envp)) < 0) { + dput(dentry); return bprm.envc; + } retval = prepare_binprm(&bprm); - if(retval>=0) { + if (retval >= 0) { bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2); bprm.exec = bprm.p; bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p,0); @@ -678,16 +690,18 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs retval = -E2BIG; } - if(retval>=0) + if (retval >= 0) retval = search_binary_handler(&bprm,regs); - if(retval>=0) + if (retval >= 0) /* execve success */ return retval; /* Something went wrong, return the inode and free the argument pages*/ - if(!bprm.dont_iput) - iput(bprm.inode); + if (bprm.dentry) + dput(bprm.dentry); + for (i=0 ; i<MAX_ARG_PAGES ; i++) free_page(bprm.page[i]); - return(retval); + + return retval; } diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 4d2b561ee..7e159e7d2 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -291,7 +291,7 @@ int ext2_new_block (const struct inode * inode, unsigned long goal, printk ("ext2_new_block: nonexistent device"); return 0; } -retry: + lock_super (sb); es = sb->u.ext2_sb.s_es; if (le32_to_cpu(es->s_free_blocks_count) <= le32_to_cpu(es->s_r_blocks_count) && @@ -299,8 +299,6 @@ retry: (sb->u.ext2_sb.s_resgid == 0 || !in_group_p (sb->u.ext2_sb.s_resgid)))) { unlock_super (sb); - if(sb->s_ibasket && free_ibasket(sb)) - goto retry; return 0; } @@ -392,8 +390,6 @@ repeat: } if (k >= sb->u.ext2_sb.s_groups_count) { unlock_super (sb); - if(sb->s_ibasket && free_ibasket(sb)) - goto retry; return 0; } bitmap_nr = load_block_bitmap (sb, i); diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index d9b1957e3..b75acdef5 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -65,6 +65,7 @@ struct inode_operations ext2_dir_inode_operations = { ext2_mknod, /* mknod */ ext2_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -211,9 +212,6 @@ revalidate: offset = 0; brelse (bh); } - if (DO_UPDATE_ATIME(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } + UPDATE_ATIME(inode); return 0; } diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 1627f5cee..d632133f1 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -72,6 +72,7 @@ struct inode_operations ext2_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ ext2_bmap, /* bmap */ @@ -121,7 +122,7 @@ static inline void remove_suid(struct inode *inode) mode &= inode->i_mode; if (mode && !suser()) { inode->i_mode &= ~mode; - inode->i_dirt = 1; + mark_inode_dirty(inode); } } @@ -250,7 +251,7 @@ static long ext2_file_write (struct inode * inode, struct file * filp, inode->u.ext2_i.i_osync--; inode->i_ctime = inode->i_mtime = CURRENT_TIME; filp->f_pos = pos; - inode->i_dirt = 1; + mark_inode_dirty(inode); return written; } diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index a486679f9..bc16722e4 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -154,8 +154,26 @@ static int load_inode_bitmap (struct super_block * sb, return 0; } +/* + * NOTE! When we get the inode, we're the only people + * that have access to it, and as such there are no + * race conditions we have to worry about. The inode + * is not on the hash-lists, and it cannot be reached + * through the filesystem because the directory entry + * has been deleted earlier. + * + * HOWEVER: we must make sure that we get no aliases, + * which means that we have to call "clear_inode()" + * _before_ we mark the inode not in use in the inode + * bitmaps. Otherwise a newly created file might use + * the same inode number (not actually the same pointer + * though), and then we'd have two inodes sharing the + * same inode number and space on the harddisk. + */ void ext2_free_inode (struct inode * inode) { + int is_directory; + unsigned long ino; struct super_block * sb; struct buffer_head * bh; struct buffer_head * bh2; @@ -171,9 +189,8 @@ void ext2_free_inode (struct inode * inode) printk ("ext2_free_inode: inode has no device\n"); return; } - if (atomic_read(&inode->i_count) > 1) { - printk ("ext2_free_inode: inode has count=%d\n", - atomic_read(&inode->i_count)); + if (inode->i_count > 1) { + printk ("ext2_free_inode: inode has count=%d\n", inode->i_count); return; } if (inode->i_nlink) { @@ -186,47 +203,53 @@ void ext2_free_inode (struct inode * inode) return; } - ext2_debug ("freeing inode %lu\n", inode->i_ino); + ino = inode->i_ino; + ext2_debug ("freeing inode %lu\n", ino); sb = inode->i_sb; lock_super (sb); - if (inode->i_ino < EXT2_FIRST_INO(sb) || - inode->i_ino > le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count)) { + if (ino < EXT2_FIRST_INO(sb) || + ino > le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count)) { ext2_error (sb, "free_inode", "reserved inode or nonexistent inode"); unlock_super (sb); return; } es = sb->u.ext2_sb.s_es; - block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(sb); - bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb); + block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb); + bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb); bitmap_nr = load_inode_bitmap (sb, block_group); bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr]; + + is_directory = S_ISDIR(inode->i_mode); + + /* Do this BEFORE marking the inode not in use */ + if (sb->dq_op) + sb->dq_op->free_inode (inode, 1); + clear_inode (inode); + + /* Ok, now we can actually update the inode bitmaps.. */ if (!ext2_clear_bit (bit, bh->b_data)) ext2_warning (sb, "ext2_free_inode", - "bit already cleared for inode %lu", inode->i_ino); + "bit already cleared for inode %lu", ino); else { gdp = get_group_desc (sb, block_group, &bh2); gdp->bg_free_inodes_count = cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) + 1); - if (S_ISDIR(inode->i_mode)) + if (is_directory) gdp->bg_used_dirs_count = cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) - 1); mark_buffer_dirty(bh2, 1); es->s_free_inodes_count = cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + 1); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); - inode->i_dirt = 0; } mark_buffer_dirty(bh, 1); if (sb->s_flags & MS_SYNCHRONOUS) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } - if (sb->dq_op) - sb->dq_op->free_inode (inode, 1); sb->s_dirt = 1; - clear_inode (inode); unlock_super (sb); } @@ -240,7 +263,7 @@ static void inc_inode_version (struct inode * inode, int mode) { inode->u.ext2_i.i_version++; - inode->i_dirt = 1; + mark_inode_dirty(inode); return; } @@ -404,7 +427,6 @@ repeat: sb->s_dirt = 1; inode->i_mode = mode; inode->i_sb = sb; - atomic_set(&inode->i_count, 1); inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; @@ -416,7 +438,7 @@ repeat: mode |= S_ISGID; } else inode->i_gid = current->fsgid; - inode->i_dirt = 1; + mark_inode_dirty(inode); inode->i_ino = j; inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ inode->i_blocks = 0; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index f2dbff2d1..0fa14cfe1 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -31,14 +31,24 @@ static int ext2_update_inode(struct inode * inode, int do_sync); +/* + * Called at each iput() + */ void ext2_put_inode (struct inode * inode) { ext2_discard_prealloc (inode); - if (inode->i_nlink || inode->i_ino == EXT2_ACL_IDX_INO || +} + +/* + * Called at the last iput() if i_nlink is zero. + */ +void ext2_delete_inode (struct inode * inode) +{ + if (inode->i_ino == EXT2_ACL_IDX_INO || inode->i_ino == EXT2_ACL_DATA_INO) return; inode->u.ext2_i.i_dtime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); ext2_update_inode(inode, IS_SYNC(inode)); inode->i_size = 0; if (inode->i_blocks) @@ -248,7 +258,7 @@ repeat: if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) ext2_sync_inode (inode); else - inode->i_dirt = 1; + mark_inode_dirty(inode); return result; } @@ -322,7 +332,7 @@ repeat: } inode->i_ctime = CURRENT_TIME; inode->i_blocks += blocks; - inode->i_dirt = 1; + mark_inode_dirty(inode); inode->u.ext2_i.i_next_alloc_block = new_block; inode->u.ext2_i.i_next_alloc_goal = tmp; brelse (bh); @@ -591,7 +601,6 @@ static int ext2_update_inode(struct inode * inode, int do_sync) else for (block = 0; block < EXT2_N_BLOCKS; block++) raw_inode->i_block[block] = cpu_to_le32(inode->u.ext2_i.i_data[block]); mark_buffer_dirty(bh, 1); - inode->i_dirt = 0; if (do_sync) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -671,7 +680,7 @@ int ext2_notify_change(struct inode *inode, struct iattr *iattr) inode->i_flags &= ~S_IMMUTABLE; inode->u.ext2_i.i_flags &= ~EXT2_IMMUTABLE_FL; } - inode->i_dirt = 1; + mark_inode_dirty(inode); return 0; } diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c index 387600bbf..c0514c01e 100644 --- a/fs/ext2/ioctl.c +++ b/fs/ext2/ioctl.c @@ -62,7 +62,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, else inode->i_flags &= ~MS_NOATIME; inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); return 0; case EXT2_IOC_GETVERSION: return put_user(inode->u.ext2_i.i_version, (int *) arg); @@ -74,7 +74,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, if (get_user(inode->u.ext2_i.i_version, (int *) arg)) return -EFAULT; inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); return 0; default: return -ENOTTY; diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 421393581..2a7c42bff 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -27,6 +27,7 @@ #include <linux/string.h> #include <linux/locks.h> + /* * define how far ahead to read directories while searching them. */ @@ -154,36 +155,26 @@ failure: return NULL; } -int ext2_lookup (struct inode * dir, const char * name, int len, - struct inode ** result) +int ext2_lookup(struct inode * dir, struct dentry *dentry) { - unsigned long ino; + struct inode * inode; struct ext2_dir_entry * de; struct buffer_head * bh; - *result = NULL; - if (!dir) - return -ENOENT; - if (!S_ISDIR(dir->i_mode)) { - iput (dir); - return -ENOTDIR; - } - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; + + bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); + inode = NULL; + if (bh) { + unsigned long ino = le32_to_cpu(de->inode); + brelse (bh); + inode = iget(dir->i_sb, ino); + + if (!inode) + return -EACCES; } - ino = dir->i_version; - if (!(bh = ext2_find_entry (dir, name, len, &de))) { - iput (dir); - return -ENOENT; - } - ino = le32_to_cpu(de->inode); - brelse (bh); - if (!(*result = iget (dir->i_sb, ino))) { - iput (dir); - return -EACCES; - } - iput (dir); + d_add(dentry, inode); return 0; } @@ -256,7 +247,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, de->inode = le32_to_cpu(0); de->rec_len = le16_to_cpu(sb->s_blocksize); dir->i_size = offset + sb->s_blocksize; - dir->i_dirt = 1; + mark_inode_dirty(dir); } else { ext2_debug ("skipping to next block\n"); @@ -301,7 +292,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, * and/or different from the directory change time. */ dir->i_mtime = dir->i_ctime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); dir->i_version = ++event; mark_buffer_dirty(bh, 1); *res_dir = de; @@ -347,31 +338,35 @@ static int ext2_delete_entry (struct ext2_dir_entry * dir, return -ENOENT; } -int ext2_create (struct inode * dir,const char * name, int len, int mode, - struct inode ** result) +/* + * By the time this is called, we already have created + * the directory cache entry for the new file, but it + * is so far negative - it has no inode. + * + * If the create succeeds, we fill in the inode information + * with d_instantiate(). + */ +int ext2_create (struct inode * dir, struct dentry * dentry, int mode) { struct inode * inode; struct buffer_head * bh; struct ext2_dir_entry * de; int err; - *result = NULL; if (!dir) return -ENOENT; inode = ext2_new_inode (dir, mode, &err); - if (!inode) { - iput (dir); + if (!inode) return err; - } + inode->i_op = &ext2_file_inode_operations; inode->i_mode = mode; - inode->i_dirt = 1; - bh = ext2_add_entry (dir, name, len, &de, &err); + mark_inode_dirty(inode); + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput (inode); - iput (dir); return err; } de->inode = cpu_to_le32(inode->i_ino); @@ -382,13 +377,11 @@ int ext2_create (struct inode * dir,const char * name, int len, int mode, wait_on_buffer (bh); } brelse (bh); - iput (dir); - *result = inode; + d_instantiate(dentry, inode); return 0; } -int ext2_mknod (struct inode * dir, const char * name, int len, int mode, - int rdev) +int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) { struct inode * inode; struct buffer_head * bh; @@ -398,21 +391,13 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode, if (!dir) return -ENOENT; - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; - } - bh = ext2_find_entry (dir, name, len, &de); - if (bh) { - brelse (bh); - iput (dir); - return -EEXIST; - } + inode = ext2_new_inode (dir, mode, &err); - if (!inode) { - iput (dir); + if (!inode) return err; - } + inode->i_uid = current->fsuid; inode->i_mode = mode; inode->i_op = NULL; @@ -433,13 +418,12 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode, init_fifo(inode); if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = to_kdev_t(rdev); - inode->i_dirt = 1; - bh = ext2_add_entry (dir, name, len, &de, &err); + mark_inode_dirty(inode); + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) { inode->i_nlink--; - inode->i_dirt = 1; - iput (inode); - iput (dir); + mark_inode_dirty(inode); + iput(inode); return err; } de->inode = cpu_to_le32(inode->i_ino); @@ -449,47 +433,34 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode, ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } - brelse (bh); - iput (dir); - iput (inode); + brelse(bh); + d_instantiate(dentry, inode); return 0; } -int ext2_mkdir (struct inode * dir, const char * name, int len, int mode) +int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) { struct inode * inode; struct buffer_head * bh, * dir_block; struct ext2_dir_entry * de; int err; - if (!dir) - return -ENOENT; - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; - } - bh = ext2_find_entry (dir, name, len, &de); - if (bh) { - brelse (bh); - iput (dir); - return -EEXIST; - } - if (dir->i_nlink >= EXT2_LINK_MAX) { - iput (dir); + + if (dir->i_nlink >= EXT2_LINK_MAX) return -EMLINK; - } + inode = ext2_new_inode (dir, S_IFDIR, &err); - if (!inode) { - iput (dir); + if (!inode) return err; - } + inode->i_op = &ext2_dir_inode_operations; inode->i_size = inode->i_sb->s_blocksize; dir_block = ext2_bread (inode, 0, 1, &err); if (!dir_block) { - iput (dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput (inode); return err; } @@ -510,12 +481,11 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode) inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; - inode->i_dirt = 1; - bh = ext2_add_entry (dir, name, len, &de, &err); + mark_inode_dirty(inode); + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) { - iput (dir); inode->i_nlink = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput (inode); return err; } @@ -527,9 +497,8 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode) wait_on_buffer (bh); } dir->i_nlink++; - dir->i_dirt = 1; - iput (dir); - iput (inode); + mark_inode_dirty(dir); + d_instantiate(dentry, inode); brelse (bh); return 0; } @@ -593,58 +562,53 @@ static int empty_dir (struct inode * inode) return 1; } -int ext2_rmdir (struct inode * dir, const char * name, int len) +int ext2_rmdir (struct inode * dir, struct dentry *dentry) { int retval; struct inode * inode; struct buffer_head * bh; struct ext2_dir_entry * de; -repeat: if (!dir) return -ENOENT; inode = NULL; - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; - } - bh = ext2_find_entry (dir, name, len, &de); + + bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); retval = -ENOENT; if (!bh) goto end_rmdir; retval = -EPERM; - if (!(inode = iget (dir->i_sb, le32_to_cpu(de->inode)))) - goto end_rmdir; + inode = dentry->d_inode; + if (inode->i_sb->dq_op) inode->i_sb->dq_op->initialize (inode, -1); - if (inode->i_dev != dir->i_dev) { - retval = -EBUSY; - goto end_rmdir; - } - if (le32_to_cpu(de->inode) != inode->i_ino) { - iput(inode); - brelse(bh); - current->counter = 0; - schedule(); - goto repeat; - } + if ((dir->i_mode & S_ISVTX) && !fsuser() && current->fsuid != inode->i_uid && current->fsuid != dir->i_uid) goto end_rmdir; if (inode == dir) /* we may not delete ".", but "../dir" is ok */ goto end_rmdir; - if (!S_ISDIR(inode->i_mode)) { - retval = -ENOTDIR; + + retval = -ENOTDIR; + if (!S_ISDIR(inode->i_mode)) goto end_rmdir; - } + + retval = -EIO; + if (inode->i_dev != dir->i_dev) + goto end_rmdir; + if (le32_to_cpu(de->inode) != inode->i_ino) + goto end_rmdir; + down(&inode->i_sem); if (!empty_dir (inode)) retval = -ENOTEMPTY; else if (le32_to_cpu(de->inode) != inode->i_ino) retval = -ENOENT; else { - if (atomic_read(&inode->i_count) > 1) { + if (inode->i_count > 1) { /* * Are we deleting the last instance of a busy directory? * Better clean up if so. @@ -671,56 +635,51 @@ repeat: (int) inode->i_nlink); inode->i_version = ++event; inode->i_nlink = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); + d_delete(dentry); + end_rmdir: - iput (dir); - iput (inode); brelse (bh); return retval; } -int ext2_unlink (struct inode * dir, const char * name, int len) +int ext2_unlink(struct inode * dir, struct dentry *dentry) { int retval; struct inode * inode; struct buffer_head * bh; struct ext2_dir_entry * de; -repeat: - if (!dir) - return -ENOENT; retval = -ENOENT; inode = NULL; - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; - } - bh = ext2_find_entry (dir, name, len, &de); + + bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); if (!bh) goto end_unlink; - if (!(inode = iget (dir->i_sb, le32_to_cpu(de->inode)))) - goto end_unlink; + + inode = dentry->d_inode; if (inode->i_sb->dq_op) inode->i_sb->dq_op->initialize (inode, -1); + retval = -EPERM; if (S_ISDIR(inode->i_mode)) goto end_unlink; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) goto end_unlink; - if (le32_to_cpu(de->inode) != inode->i_ino) { - iput(inode); - brelse(bh); - current->counter = 0; - schedule(); - goto repeat; - } if ((dir->i_mode & S_ISVTX) && !fsuser() && current->fsuid != inode->i_uid && current->fsuid != dir->i_uid) goto end_unlink; + + retval = -EIO; + if (le32_to_cpu(de->inode) != inode->i_ino) + goto end_unlink; + if (!inode->i_nlink) { ext2_warning (inode->i_sb, "ext2_unlink", "Deleting nonexistent file (%lu), %d", @@ -737,20 +696,19 @@ repeat: wait_on_buffer (bh); } dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime; retval = 0; + d_delete(dentry); /* This also frees the inode */ + end_unlink: brelse (bh); - iput (inode); - iput (dir); return retval; } -int ext2_symlink (struct inode * dir, const char * name, int len, - const char * symname) +int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symname) { struct ext2_dir_entry * de; struct inode * inode = NULL; @@ -761,7 +719,6 @@ int ext2_symlink (struct inode * dir, const char * name, int len, char c; if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) { - iput (dir); return err; } inode->i_mode = S_IFLNK | S_IRWXUGO; @@ -775,9 +732,8 @@ int ext2_symlink (struct inode * dir, const char * name, int len, name_block = ext2_bread (inode, 0, 1, &err); if (!name_block) { - iput (dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput (inode); return err; } @@ -797,23 +753,13 @@ int ext2_symlink (struct inode * dir, const char * name, int len, brelse (name_block); } inode->i_size = i; - inode->i_dirt = 1; + mark_inode_dirty(inode); - bh = ext2_find_entry (dir, name, len, &de); - if (bh) { - inode->i_nlink--; - inode->i_dirt = 1; - iput (inode); - brelse (bh); - iput (dir); - return -EEXIST; - } - bh = ext2_add_entry (dir, name, len, &de, &err); + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput (inode); - iput (dir); return err; } de->inode = cpu_to_le32(inode->i_ino); @@ -824,47 +770,30 @@ int ext2_symlink (struct inode * dir, const char * name, int len, wait_on_buffer (bh); } brelse (bh); - iput (dir); - iput (inode); + d_instantiate(dentry, inode); return 0; } -int ext2_link (struct inode * oldinode, struct inode * dir, - const char * name, int len) +int ext2_link (struct inode * inode, struct inode * dir, struct dentry *dentry) { struct ext2_dir_entry * de; struct buffer_head * bh; int err; - if (S_ISDIR(oldinode->i_mode)) { - iput (oldinode); - iput (dir); + if (S_ISDIR(inode->i_mode)) return -EPERM; - } - if (IS_APPEND(oldinode) || IS_IMMUTABLE(oldinode)) { - iput (oldinode); - iput (dir); + + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return -EPERM; - } - if (oldinode->i_nlink >= EXT2_LINK_MAX) { - iput (oldinode); - iput (dir); + + if (inode->i_nlink >= EXT2_LINK_MAX) return -EMLINK; - } - bh = ext2_find_entry (dir, name, len, &de); - if (bh) { - brelse (bh); - iput (dir); - iput (oldinode); - return -EEXIST; - } - bh = ext2_add_entry (dir, name, len, &de, &err); - if (!bh) { - iput (dir); - iput (oldinode); + + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); + if (!bh) return err; - } - de->inode = cpu_to_le32(oldinode->i_ino); + + de->inode = cpu_to_le32(inode->i_ino); dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -872,35 +801,33 @@ int ext2_link (struct inode * oldinode, struct inode * dir, wait_on_buffer (bh); } brelse (bh); - iput (dir); - oldinode->i_nlink++; - oldinode->i_ctime = CURRENT_TIME; - oldinode->i_dirt = 1; - iput (oldinode); + inode->i_nlink++; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + inode->i_count++; + d_instantiate(dentry, inode); return 0; } -static int subdir (struct inode * new_inode, struct inode * old_inode) +/* + * Trivially implemented using the dcache structure + */ +static int subdir (struct dentry * new_dentry, struct dentry * old_dentry) { - int ino; int result; - atomic_inc(&new_inode->i_count); result = 0; for (;;) { - if (new_inode == old_inode) { - result = 1; - break; + if (new_dentry != old_dentry) { + struct dentry * parent = new_dentry->d_parent; + if (parent == new_dentry) + break; + new_dentry = parent; + continue; } - if (new_inode->i_dev != old_inode->i_dev) - break; - ino = new_inode->i_ino; - if (ext2_lookup (new_inode, "..", 2, &new_inode)) - break; - if (new_inode->i_ino == ino) - break; + result = 1; + break; } - iput (new_inode); return result; } @@ -908,10 +835,6 @@ static int subdir (struct inode * new_inode, struct inode * old_inode) ((struct ext2_dir_entry *) ((char *) buffer + \ le16_to_cpu(((struct ext2_dir_entry *) buffer)->rec_len)))->inode -#define PARENT_NAME(buffer) \ - ((struct ext2_dir_entry *) ((char *) buffer + \ - le16_to_cpu(((struct ext2_dir_entry *) buffer)->rec_len)))->name - /* * rename uses retrying to avoid race-conditions: at least they should be * minimal. @@ -923,43 +846,27 @@ static int subdir (struct inode * new_inode, struct inode * old_inode) * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ -static int do_ext2_rename (struct inode * old_dir, const char * old_name, - int old_len, struct inode * new_dir, - const char * new_name, int new_len) +static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry, + struct inode * new_dir,struct dentry *new_dentry) { struct inode * old_inode, * new_inode; struct buffer_head * old_bh, * new_bh, * dir_bh; struct ext2_dir_entry * old_de, * new_de; int retval; - goto start_up; -try_again: - if (new_bh && new_de) { - ext2_delete_entry(new_de, new_bh); - new_dir->i_version = ++event; - } - brelse (old_bh); - brelse (new_bh); - brelse (dir_bh); - iput (old_inode); - iput (new_inode); - current->counter = 0; - schedule (); -start_up: old_inode = new_inode = NULL; old_bh = new_bh = dir_bh = NULL; new_de = NULL; retval = -ENAMETOOLONG; - if (old_len > EXT2_NAME_LEN) + if (old_dentry->d_name.len > EXT2_NAME_LEN) goto end_rename; - old_bh = ext2_find_entry (old_dir, old_name, old_len, &old_de); + old_bh = ext2_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); retval = -ENOENT; if (!old_bh) goto end_rename; - old_inode = __iget (old_dir->i_sb, le32_to_cpu(old_de->inode), 0); /* don't cross mnt-points */ - if (!old_inode) - goto end_rename; + old_inode = old_dentry->d_inode; + retval = -EPERM; if ((old_dir->i_mode & S_ISVTX) && current->fsuid != old_inode->i_uid && @@ -967,9 +874,10 @@ start_up: goto end_rename; if (IS_APPEND(old_inode) || IS_IMMUTABLE(old_inode)) goto end_rename; - new_bh = ext2_find_entry (new_dir, new_name, new_len, &new_de); + + new_inode = new_dentry->d_inode; + new_bh = ext2_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de); if (new_bh) { - new_inode = __iget (new_dir->i_sb, le32_to_cpu(new_de->inode), 0); /* no mntp cross */ if (!new_inode) { brelse (new_bh); new_bh = NULL; @@ -987,13 +895,13 @@ start_up: if (!S_ISDIR(old_inode->i_mode)) goto end_rename; retval = -EINVAL; - if (subdir (new_dir, old_inode)) + if (subdir(new_dentry, old_dentry)) goto end_rename; retval = -ENOTEMPTY; if (!empty_dir (new_inode)) goto end_rename; retval = -EBUSY; - if (atomic_read(&new_inode->i_count) > 1) + if (new_inode->i_count > 1) goto end_rename; } retval = -EPERM; @@ -1006,7 +914,7 @@ start_up: if (new_inode && !S_ISDIR(new_inode->i_mode)) goto end_rename; retval = -EINVAL; - if (subdir (new_dir, old_inode)) + if (subdir(new_dentry, old_dentry)) goto end_rename; dir_bh = ext2_bread (old_inode, 0, 0, &retval); if (!dir_bh) @@ -1018,48 +926,37 @@ start_up: goto end_rename; } if (!new_bh) - new_bh = ext2_add_entry (new_dir, new_name, new_len, &new_de, + new_bh = ext2_add_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de, &retval); if (!new_bh) goto end_rename; new_dir->i_version = ++event; - /* - * sanity checking before doing the rename - avoid races - */ - if (new_inode && (le32_to_cpu(new_de->inode) != new_inode->i_ino)) - goto try_again; - if (le32_to_cpu(new_de->inode) && !new_inode) - goto try_again; - if (le32_to_cpu(old_de->inode) != old_inode->i_ino) - goto try_again; + /* * ok, that's it */ new_de->inode = le32_to_cpu(old_inode->i_ino); - retval = ext2_delete_entry (old_de, old_bh); - if (retval == -ENOENT) - goto try_again; - if (retval) - goto end_rename; + ext2_delete_entry (old_de, old_bh); + old_dir->i_version = ++event; if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); } old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; - old_dir->i_dirt = 1; + mark_inode_dirty(old_dir); if (dir_bh) { PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino); mark_buffer_dirty(dir_bh, 1); old_dir->i_nlink--; - old_dir->i_dirt = 1; + mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink--; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); } else { new_dir->i_nlink++; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); } } mark_buffer_dirty(old_bh, 1); @@ -1072,15 +969,15 @@ start_up: ll_rw_block (WRITE, 1, &new_bh); wait_on_buffer (new_bh); } + + /* Update the dcache */ + d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name); + d_delete(new_dentry); retval = 0; end_rename: brelse (dir_bh); brelse (old_bh); brelse (new_bh); - iput (old_inode); - iput (new_inode); - iput (old_dir); - iput (new_dir); return retval; } @@ -1097,16 +994,15 @@ end_rename: * super-block. This way, we really lock other renames only if they occur * on the same file system */ -int ext2_rename (struct inode * old_dir, const char * old_name, int old_len, - struct inode * new_dir, const char * new_name, int new_len) +int ext2_rename (struct inode * old_dir, struct dentry *old_dentry, + struct inode * new_dir, struct dentry *new_dentry) { int result; while (old_dir->i_sb->u.ext2_sb.s_rename_lock) sleep_on (&old_dir->i_sb->u.ext2_sb.s_rename_wait); old_dir->i_sb->u.ext2_sb.s_rename_lock = 1; - result = do_ext2_rename (old_dir, old_name, old_len, new_dir, - new_name, new_len); + result = do_ext2_rename (old_dir, old_dentry, new_dir, new_dentry); old_dir->i_sb->u.ext2_sb.s_rename_lock = 0; wake_up (&old_dir->i_sb->u.ext2_sb.s_rename_wait); return result; diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 635a45692..1adc82185 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -133,9 +133,10 @@ void ext2_put_super (struct super_block * sb) static struct super_operations ext2_sops = { ext2_read_inode, - NULL, ext2_write_inode, ext2_put_inode, + ext2_delete_inode, + NULL, ext2_put_super, ext2_write_super, ext2_statfs, @@ -632,7 +633,8 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, */ sb->s_dev = dev; sb->s_op = &ext2_sops; - if (!(sb->s_mounted = iget (sb, EXT2_ROOT_INO))) { + sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO), NULL); + if (!sb->s_root) { sb->s_dev = 0; for (i = 0; i < db_count; i++) if (sb->u.ext2_sb.s_group_desc[i]) @@ -761,7 +763,7 @@ void cleanup_module(void) #endif -void ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz) +int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz) { unsigned long overhead; unsigned long overhead_per_group; @@ -792,5 +794,5 @@ void ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz) tmp.f_files = le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count); tmp.f_ffree = ext2_count_free_inodes (sb); tmp.f_namelen = EXT2_NAME_LEN; - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index 4d5a5cada..781f9165d 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -25,6 +25,7 @@ #include <linux/stat.h> static int ext2_readlink (struct inode *, char *, int); +static struct dentry *ext2_follow_link(struct inode *, struct dentry *); /* * symlinks can't do much... @@ -41,6 +42,7 @@ struct inode_operations ext2_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ ext2_readlink, /* readlink */ + ext2_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -49,37 +51,54 @@ struct inode_operations ext2_symlink_inode_operations = { NULL /* smap */ }; +static struct dentry * ext2_follow_link(struct inode * inode, struct dentry *base) +{ + int error; + struct buffer_head * bh = NULL; + char * link; + + link = (char *) inode->u.ext2_i.i_data; + if (inode->i_blocks) { + if (!(bh = ext2_bread (inode, 0, 0, &error))) { + dput(base); + return ERR_PTR(-EIO); + } + link = bh->b_data; + } + UPDATE_ATIME(inode); + base = lookup_dentry(link, base, 1); + if (bh) + brelse(bh); + return base; +} + static int ext2_readlink (struct inode * inode, char * buffer, int buflen) { struct buffer_head * bh = NULL; char * link; - int i, err; + int i; if (buflen > inode->i_sb->s_blocksize - 1) buflen = inode->i_sb->s_blocksize - 1; + + link = (char *) inode->u.ext2_i.i_data; if (inode->i_blocks) { + int err; bh = ext2_bread (inode, 0, 0, &err); if (!bh) { - iput (inode); if(err < 0) /* indicate type of error */ return err; return 0; } link = bh->b_data; } - else - link = (char *) inode->u.ext2_i.i_data; i = 0; while (i < buflen && link[i]) i++; if (copy_to_user(buffer, link, i)) i = -EFAULT; - if (DO_UPDATE_ATIME(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } - iput (inode); + UPDATE_ATIME(inode); if (bh) brelse (bh); return i; diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c index a9e59ca00..5933ff77c 100644 --- a/fs/ext2/truncate.c +++ b/fs/ext2/truncate.c @@ -91,7 +91,7 @@ repeat: } *p = 0; inode->i_blocks -= blocks; - inode->i_dirt = 1; + mark_inode_dirty(inode); bforget(bh); if (free_count == 0) { block_to_free = tmp; @@ -110,7 +110,8 @@ repeat: return retry; } -static int trunc_indirect (struct inode * inode, int offset, u32 * p) +static int trunc_indirect (struct inode * inode, int offset, u32 * p, + int in_inode) { int i, tmp; struct buffer_head * bh; @@ -124,103 +125,16 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p) #define INDIRECT_BLOCK ((int)DIRECT_BLOCK - offset) int indirect_block = INDIRECT_BLOCK; - tmp = *p; + tmp = in_inode ? *p : le32_to_cpu(*p); if (!tmp) return 0; ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != *p) { - brelse (ind_bh); - return 1; - } - if (!ind_bh) { - *p = 0; - return 0; - } -repeat: - for (i = indirect_block ; i < addr_per_block ; i++) { - if (i < 0) - i = 0; - if (i < indirect_block) - goto repeat; - ind = i + (u32 *) ind_bh->b_data; - tmp = le32_to_cpu(*ind); - if (!tmp) - continue; - bh = get_hash_table (inode->i_dev, tmp, - inode->i_sb->s_blocksize); - if (i < indirect_block) { - brelse (bh); - goto repeat; - } - if ((bh && bh->b_count != 1) || tmp != le32_to_cpu(*ind)) { - retry = 1; - brelse (bh); - continue; - } - *ind = cpu_to_le32(0); - mark_buffer_dirty(ind_bh, 1); - bforget(bh); - if (free_count == 0) { - block_to_free = tmp; - free_count++; - } else if (free_count > 0 && block_to_free == tmp - free_count) - free_count++; - else { - ext2_free_blocks (inode, block_to_free, free_count); - block_to_free = tmp; - free_count = 1; - } -/* ext2_free_blocks (inode, tmp, 1); */ - inode->i_blocks -= blocks; - inode->i_dirt = 1; - } - if (free_count > 0) - ext2_free_blocks (inode, block_to_free, free_count); - ind = (u32 *) ind_bh->b_data; - for (i = 0; i < addr_per_block; i++) - if (le32_to_cpu(*(ind++))) - break; - if (i >= addr_per_block) - if (ind_bh->b_count != 1) - retry = 1; - else { - tmp = *p; - *p = 0; - inode->i_blocks -= blocks; - inode->i_dirt = 1; - ext2_free_blocks (inode, tmp, 1); - } - if (IS_SYNC(inode) && buffer_dirty(ind_bh)) { - ll_rw_block (WRITE, 1, &ind_bh); - wait_on_buffer (ind_bh); - } - brelse (ind_bh); - return retry; -} - -static int trunc_indirect_swab32 (struct inode * inode, int offset, u32 * p) -{ - int i, tmp; - struct buffer_head * bh; - struct buffer_head * ind_bh; - u32 * ind; - unsigned long block_to_free = 0; - unsigned long free_count = 0; - int retry = 0; - int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); - int blocks = inode->i_sb->s_blocksize / 512; - int indirect_block = INDIRECT_BLOCK; - - tmp = le32_to_cpu(*p); - if (!tmp) - return 0; - ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != le32_to_cpu(*p)) { + if (tmp != (in_inode ? *p : le32_to_cpu(*p))) { brelse (ind_bh); return 1; } if (!ind_bh) { - *p = cpu_to_le32(0); + *p = in_inode ? 0 : cpu_to_le32(0); return 0; } repeat: @@ -259,7 +173,7 @@ repeat: } /* ext2_free_blocks (inode, tmp, 1); */ inode->i_blocks -= blocks; - inode->i_dirt = 1; + mark_inode_dirty(inode); } if (free_count > 0) ext2_free_blocks (inode, block_to_free, free_count); @@ -271,13 +185,15 @@ repeat: if (ind_bh->b_count != 1) retry = 1; else { - tmp = le32_to_cpu(*p); - *p = cpu_to_le32(0); + tmp = in_inode ? *p : le32_to_cpu(*p); + *p = in_inode ? 0 : cpu_to_le32(0); inode->i_blocks -= blocks; - inode->i_dirt = 1; + mark_inode_dirty(inode); ext2_free_blocks (inode, tmp, 1); + bforget(ind_bh); + ind_bh = NULL; } - if (IS_SYNC(inode) && buffer_dirty(ind_bh)) { + if (IS_SYNC(inode) && ind_bh && buffer_dirty(ind_bh)) { ll_rw_block (WRITE, 1, &ind_bh); wait_on_buffer (ind_bh); } @@ -286,7 +202,7 @@ repeat: } static int trunc_dindirect (struct inode * inode, int offset, - u32 * p) + u32 * p, int in_inode) { int i, tmp; struct buffer_head * dind_bh; @@ -297,75 +213,16 @@ static int trunc_dindirect (struct inode * inode, int offset, #define DINDIRECT_BLOCK (((int)DIRECT_BLOCK - offset) / addr_per_block) int dindirect_block = DINDIRECT_BLOCK; - tmp = *p; - if (!tmp) - return 0; - dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != *p) { - brelse (dind_bh); - return 1; - } - if (!dind_bh) { - *p = 0; - return 0; - } -repeat: - for (i = dindirect_block ; i < addr_per_block ; i++) { - if (i < 0) - i = 0; - if (i < dindirect_block) - goto repeat; - dind = i + (u32 *) dind_bh->b_data; - tmp = le32_to_cpu(*dind); - if (!tmp) - continue; - retry |= trunc_indirect_swab32 (inode, offset + (i * addr_per_block), - dind); - mark_buffer_dirty(dind_bh, 1); - } - dind = (u32 *) dind_bh->b_data; - for (i = 0; i < addr_per_block; i++) - if (le32_to_cpu(*(dind++))) - break; - if (i >= addr_per_block) - if (dind_bh->b_count != 1) - retry = 1; - else { - tmp = *p; - *p = 0; - inode->i_blocks -= blocks; - inode->i_dirt = 1; - ext2_free_blocks (inode, tmp, 1); - } - if (IS_SYNC(inode) && buffer_dirty(dind_bh)) { - ll_rw_block (WRITE, 1, &dind_bh); - wait_on_buffer (dind_bh); - } - brelse (dind_bh); - return retry; -} - -static int trunc_dindirect_swab32 (struct inode * inode, int offset, - u32 * p) -{ - int i, tmp; - struct buffer_head * dind_bh; - u32 * dind; - int retry = 0; - int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); - int blocks = inode->i_sb->s_blocksize / 512; - int dindirect_block = DINDIRECT_BLOCK; - - tmp = le32_to_cpu(*p); + tmp = in_inode ? *p : le32_to_cpu(*p); if (!tmp) return 0; dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != le32_to_cpu(*p)) { + if (tmp != (in_inode ? *p : le32_to_cpu(*p))) { brelse (dind_bh); return 1; } if (!dind_bh) { - *p = cpu_to_le32(0); + *p = in_inode ? 0 : cpu_to_le32(0); return 0; } repeat: @@ -378,8 +235,8 @@ repeat: tmp = le32_to_cpu(*dind); if (!tmp) continue; - retry |= trunc_indirect_swab32 (inode, offset + (i * addr_per_block), - dind); + retry |= trunc_indirect(inode, offset + (i * addr_per_block), + dind, 0); mark_buffer_dirty(dind_bh, 1); } dind = (u32 *) dind_bh->b_data; @@ -390,13 +247,15 @@ repeat: if (dind_bh->b_count != 1) retry = 1; else { - tmp = le32_to_cpu(*p); - *p = cpu_to_le32(0); + tmp = in_inode ? *p : le32_to_cpu(*p); + *p = in_inode ? 0 : cpu_to_le32(0); inode->i_blocks -= blocks; - inode->i_dirt = 1; + mark_inode_dirty(inode); ext2_free_blocks (inode, tmp, 1); + bforget(dind_bh); + dind_bh = 0; } - if (IS_SYNC(inode) && buffer_dirty(dind_bh)) { + if (IS_SYNC(inode) && dind_bh && buffer_dirty(dind_bh)) { ll_rw_block (WRITE, 1, &dind_bh); wait_on_buffer (dind_bh); } @@ -436,9 +295,9 @@ repeat: if (i < tindirect_block) goto repeat; tind = i + (u32 *) tind_bh->b_data; - retry |= trunc_dindirect_swab32(inode, EXT2_NDIR_BLOCKS + + retry |= trunc_dindirect(inode, EXT2_NDIR_BLOCKS + addr_per_block + (i + 1) * addr_per_block * addr_per_block, - tind); + tind, 0); mark_buffer_dirty(tind_bh, 1); } tind = (u32 *) tind_bh->b_data; @@ -452,10 +311,12 @@ repeat: tmp = *p; *p = 0; inode->i_blocks -= blocks; - inode->i_dirt = 1; + mark_inode_dirty(inode); ext2_free_blocks (inode, tmp, 1); + bforget(tind_bh); + tind_bh = 0; } - if (IS_SYNC(inode) && buffer_dirty(tind_bh)) { + if (IS_SYNC(inode) && tind_bh && buffer_dirty(tind_bh)) { ll_rw_block (WRITE, 1, &tind_bh); wait_on_buffer (tind_bh); } @@ -479,14 +340,14 @@ void ext2_truncate (struct inode * inode) while (1) { retry = trunc_direct(inode); retry |= trunc_indirect (inode, EXT2_IND_BLOCK, - (u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK]); + (u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK], 1); retry |= trunc_dindirect (inode, EXT2_IND_BLOCK + EXT2_ADDR_PER_BLOCK(inode->i_sb), - (u32 *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK]); + (u32 *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK], 1); retry |= trunc_tindirect (inode); if (!retry) break; - if (IS_SYNC(inode) && inode->i_dirt) + if (IS_SYNC(inode) && test_bit(I_DIRTY, &inode->i_state)) ext2_sync_inode (inode); current->counter = 0; schedule (); @@ -510,5 +371,5 @@ void ext2_truncate (struct inode * inode) } } inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 62ff8af1e..6223c1c48 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -280,7 +280,7 @@ int fat_free(struct inode *inode,int skip) 12 ? EOF_FAT12 : EOF_FAT16); else { MSDOS_I(inode)->i_start = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); } lock_fat(inode->i_sb); while (nr != -1) { diff --git a/fs/fat/file.c b/fs/fat/file.c index 82787075a..ca35cc28f 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -52,6 +52,7 @@ struct inode_operations fat_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ fat_bmap, /* bmap */ @@ -99,6 +100,7 @@ struct inode_operations fat_file_inode_operations_1024 = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -355,7 +357,7 @@ long fat_file_write( filp->f_pos += written; if (filp->f_pos > inode->i_size) { inode->i_size = filp->f_pos; - inode->i_dirt = 1; + mark_inode_dirty(inode); } fat_set_uptodate(sb, bh, 1); fat_mark_buffer_dirty(sb, bh, 0); @@ -365,7 +367,7 @@ long fat_file_write( return error; inode->i_mtime = inode->i_ctime = CURRENT_TIME; MSDOS_I(inode)->i_attrs |= ATTR_ARCH; - inode->i_dirt = 1; + mark_inode_dirty(inode); return buf-start; } @@ -379,5 +381,5 @@ void fat_truncate(struct inode *inode) cluster = SECTOR_SIZE*MSDOS_SB(inode->i_sb)->cluster_size; (void) fat_free(inode,(inode->i_size+(cluster-1))/cluster); MSDOS_I(inode)->i_attrs |= ATTR_ARCH; - inode->i_dirt = 1; + mark_inode_dirty(inode); } diff --git a/fs/fat/inode.c b/fs/fat/inode.c index cf14856d1..e35722aff 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -196,6 +196,7 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent int debug,error,fat; int blksize = 512; struct fat_mount_options opts; + struct inode *root_inode; MOD_INC_USE_COUNT; if (hardsect_size[MAJOR(sb->s_dev)] != NULL){ @@ -329,7 +330,10 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent MSDOS_SB(sb)->fat_lock = 0; MSDOS_SB(sb)->prev_free = 0; memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options)); - if (!(sb->s_mounted = iget(sb,MSDOS_ROOT_INO))) { + + root_inode = iget(sb,MSDOS_ROOT_INO); + sb->s_root = d_alloc_root(root_inode, NULL); + if (!sb->s_root) { sb->s_dev = 0; printk("get root inode failed\n"); MOD_DEC_USE_COUNT; @@ -339,7 +343,7 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent } -void fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz) +int fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz) { int free,nr; struct statfs tmp; @@ -362,7 +366,7 @@ void fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz) tmp.f_files = 0; tmp.f_ffree = 0; tmp.f_namelen = 12; - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } @@ -514,10 +518,9 @@ void fat_write_inode(struct inode *inode) linked->i_blocks = inode->i_blocks; linked->i_atime = inode->i_atime; MSDOS_I(linked)->i_attrs = MSDOS_I(inode)->i_attrs; - linked->i_dirt = 1; + mark_inode_dirty(linked); } - inode->i_dirt = 0; if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return; if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) { printk("dev = %s, ino = %ld\n", diff --git a/fs/fat/misc.c b/fs/fat/misc.c index d1e9bc6ca..034f62c1f 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -19,13 +19,14 @@ /* Well-known binary file extensions - of course there are many more */ -static char bin_extensions[] = - "EXE" "COM" "BIN" "APP" "SYS" "DRV" "OVL" "OVR" "OBJ" "LIB" "DLL" "PIF" /* program code */ - "ARC" "ZIP" "LHA" "LZH" "ZOO" "TAR" "Z " "ARJ" /* common archivers */ - "TZ " "TAZ" "TZP" "TPZ" /* abbreviations of tar.Z and tar.zip */ - "GZ " "TGZ" "DEB" /* .gz, .tar.gz and Debian packages */ - "GIF" "BMP" "TIF" "GL " "JPG" "PCX" /* graphics */ - "TFM" "VF " "GF " "PK " "PXL" "DVI"; /* TeX */ +static char ascii_extensions[] = + "TXT" "ME " "HTM" "1ST" "LOG" " " /* text files */ + "C " "H " "CPP" "LIS" "PAS" "FOR" /* programming languages */ + "F " "MAK" "INC" "BAS" /* programming languages */ + "BAT" "SH " /* program code :) */ + "INI" /* config files */ + "PBM" "PGM" "DXF" /* graphics */ + "TEX"; /* TeX */ /* @@ -39,9 +40,7 @@ void fat_fs_panic(struct super_block *s,const char *msg) not_ro = !(s->s_flags & MS_RDONLY); if (not_ro) s->s_flags |= MS_RDONLY; - printk("Filesystem panic (dev %s, ", kdevname(s->s_dev)); - printk("mounted on %s:%ld)\n %s\n", /* note: kdevname returns & static char[] */ - kdevname(s->s_covered->i_dev), s->s_covered->i_ino, msg); + printk("Filesystem panic (dev %s).", kdevname(s->s_dev)); if (not_ro) printk(" File system has been set read-only\n"); } @@ -62,9 +61,9 @@ int is_binary(char conversion,char *extension) case 't': return 0; case 'a': - for (walk = bin_extensions; *walk; walk += 3) - if (!strncmp(extension,walk,3)) return 1; - return 0; + for (walk = ascii_extensions; *walk; walk += 3) + if (!strncmp(extension,walk,3)) return 0; + return 1; /* default binary conversion */ default: printk("Invalid conversion mode - defaulting to " "binary.\n"); @@ -179,7 +178,7 @@ printk("last = %d\n",last); if (last) fat_access(sb,last,nr); else { MSDOS_I(inode)->i_start = nr; - inode->i_dirt = 1; + mark_inode_dirty(inode); } #ifdef DEBUG if (last) printk("next set to %d\n",fat_access(sb,last,-1)); @@ -216,7 +215,7 @@ if (last) printk("next set to %d\n",fat_access(sb,last,-1)); #ifdef DEBUG printk("size is %d now (%x)\n",inode->i_size,inode); #endif - inode->i_dirt = 1; + mark_inode_dirty(inode); } return 0; } diff --git a/fs/fat/mmap.c b/fs/fat/mmap.c index 6a3515eef..a858cd2cc 100644 --- a/fs/fat/mmap.c +++ b/fs/fat/mmap.c @@ -29,7 +29,7 @@ static unsigned long fat_file_mmap_nopage( unsigned long address, int error_code) { - struct inode * inode = area->vm_inode; + struct inode * inode = area->vm_dentry->d_inode; unsigned long page; unsigned int clear; int pos; @@ -101,11 +101,10 @@ int fat_mmap(struct inode * inode, struct file * file, struct vm_area_struct * v return -EACCES; if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + vma->vm_dentry = dget(file->f_dentry); vma->vm_ops = &fat_file_mmap; return 0; } diff --git a/fs/fcntl.c b/fs/fcntl.c index 6418a5d83..57eef2530 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -67,6 +67,34 @@ asmlinkage int sys_dup(unsigned int fildes) return ret; } +#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC) + +static int setfl(struct file * filp, unsigned long arg) +{ + struct inode * inode = filp->f_dentry->d_inode; + + /* + * In the case of an append-only file, O_APPEND + * cannot be cleared + */ + if (!(arg & O_APPEND) && IS_APPEND(inode)) + return -EPERM; + + /* Did FASYNC state change? */ + if ((arg ^ filp->f_flags) & FASYNC) { + if (filp->f_op->fasync) + filp->f_op->fasync(inode, filp, (arg & FASYNC) != 0); + } + + /* required for strict SunOS emulation */ + if (O_NONBLOCK != O_NDELAY) + if (arg & O_NDELAY) + arg |= O_NONBLOCK; + + filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK); + return 0; +} + asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) { struct file * filp; @@ -95,28 +123,7 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) err = filp->f_flags; break; case F_SETFL: - /* - * In the case of an append-only file, O_APPEND - * cannot be cleared - */ - err = -EPERM; - if (IS_APPEND(filp->f_inode) && !(arg & O_APPEND)) - break; - err = 0; - if ((arg & FASYNC) && !(filp->f_flags & FASYNC) && - filp->f_op->fasync) - filp->f_op->fasync(filp->f_inode, filp, 1); - if (!(arg & FASYNC) && (filp->f_flags & FASYNC) && - filp->f_op->fasync) - filp->f_op->fasync(filp->f_inode, filp, 0); - /* required for strict SunOS emulation */ - if (O_NONBLOCK != O_NDELAY) - if (arg & O_NDELAY) - arg |= O_NONBLOCK; - filp->f_flags &= ~(O_APPEND | O_NONBLOCK | - O_NDELAY | FASYNC); - filp->f_flags |= arg & (O_APPEND | O_NONBLOCK | - O_NDELAY | FASYNC); + err = setfl(filp, arg); break; case F_GETLK: err = fcntl_getlk(fd, (struct flock *) arg); @@ -186,12 +193,12 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) fasync_ok: err = 0; filp->f_owner = arg; - if (S_ISSOCK (filp->f_inode->i_mode)) + if (S_ISSOCK (filp->f_dentry->d_inode->i_mode)) err = sock_fcntl (filp, F_SETOWN, arg); break; default: /* sockets need a few special fcntls. */ - if (S_ISSOCK (filp->f_inode->i_mode)) + if (S_ISSOCK (filp->f_dentry->d_inode->i_mode)) err = sock_fcntl (filp, cmd, arg); else err = -EINVAL; @@ -153,7 +153,6 @@ struct inode_operations fifo_inode_operations = { void init_fifo(struct inode * inode) { inode->i_op = &fifo_inode_operations; - inode->i_pipe = 1; PIPE_LOCK(*inode) = 0; PIPE_BASE(*inode) = NULL; PIPE_START(*inode) = PIPE_LEN(*inode) = 0; diff --git a/fs/file_table.c b/fs/file_table.c index b8c8d4155..6413218ff 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -102,6 +102,24 @@ again: return f; } +/* + * Clear and initialize a (private) struct file for the given dentry, + * and call the open function (if any). The caller must verify that + * inode->i_op and inode->i_op->default_file_ops are not NULL. + */ +int init_private_file(struct file *filp, struct dentry *dentry, int mode) +{ + memset(filp, 0, sizeof(*filp)); + filp->f_mode = mode; + filp->f_count = 1; + filp->f_dentry = dentry; + filp->f_op = dentry->d_inode->i_op->default_file_ops; + if (filp->f_op->open) + return filp->f_op->open(dentry->d_inode, filp); + else + return 0; +} + #ifdef CONFIG_QUOTA void add_dquot_ref(kdev_t dev, short type) @@ -109,11 +127,15 @@ void add_dquot_ref(kdev_t dev, short type) struct file *filp; for (filp = inuse_filps; filp; filp = filp->f_next) { - if (!filp->f_inode || filp->f_inode->i_dev != dev) + struct inode * inode; + if (!filp->f_dentry) + continue; + inode = filp->f_dentry->d_inode; + if (!inode || inode->i_dev != dev) continue; - if (filp->f_mode & FMODE_WRITE && filp->f_inode->i_sb->dq_op) { - filp->f_inode->i_sb->dq_op->initialize(filp->f_inode, type); - filp->f_inode->i_flags |= S_WRITE; + if (filp->f_mode & FMODE_WRITE && inode->i_sb->dq_op) { + inode->i_sb->dq_op->initialize(inode, type); + inode->i_flags |= S_WRITE; } } } @@ -123,11 +145,15 @@ void reset_dquot_ptrs(kdev_t dev, short type) struct file *filp; for (filp = inuse_filps; filp; filp = filp->f_next) { - if (!filp->f_inode || filp->f_inode->i_dev != dev) + struct inode * inode; + if (!filp->f_dentry) + continue; + inode = filp->f_dentry->d_inode; + if (!inode || inode->i_dev != dev) continue; - if (IS_WRITABLE(filp->f_inode)) { - filp->f_inode->i_dquot[type] = NODQUOT; - filp->f_inode->i_flags &= ~S_WRITE; + if (IS_WRITABLE(inode)) { + inode->i_dquot[type] = NODQUOT; + inode->i_flags &= ~S_WRITE; } } } diff --git a/fs/filesystems.c b/fs/filesystems.c index 004ee0aff..74016aa67 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -8,7 +8,6 @@ #include <linux/config.h> #include <linux/fs.h> -#include <linux/nametrans.h> #include <linux/minix_fs.h> #include <linux/ext2_fs.h> @@ -45,10 +44,6 @@ __initfunc(static void do_sys_setup(void)) binfmt_setup(); -#ifdef CONFIG_TRANS_NAMES - init_nametrans(); -#endif - #ifdef CONFIG_EXT2_FS init_ext2_fs(); #endif diff --git a/fs/inode.c b/fs/inode.c index 7215e1204..8813bbd45 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1,708 +1,500 @@ /* - * fs/inode.c + * linux/fs/inode.c * - * Complete reimplementation - * (C) 1997 Thomas Schoebel-Theuer + * (C) 1997 Linus Torvalds */ -/* Everything here is intended to be MP-safe. However, other parts - * of the kernel are not yet MP-safe, in particular the inode->i_count++ - * that are spread over everywhere. These should be replaced by - * iinc() as soon as possible. Since I have no MP machine, I could - * not test it. - */ -#include <linux/config.h> -#include <linux/errno.h> #include <linux/fs.h> #include <linux/string.h> #include <linux/mm.h> -#include <linux/kernel.h> -#include <linux/dlists.h> -#include <linux/dalloc.h> -#include <linux/omirr.h> - -/* #define DEBUG */ - -#define HASH_SIZE 1024 /* must be a power of 2 */ -#define NR_LEVELS 4 - -#define ST_AGED 1 -#define ST_HASHED 2 -#define ST_EMPTY 4 -#define ST_TO_READ 8 -#define ST_TO_WRITE 16 -#define ST_TO_PUT 32 -#define ST_TO_DROP 64 -#define ST_IO (ST_TO_READ|ST_TO_WRITE|ST_TO_PUT|ST_TO_DROP) -#define ST_WAITING 128 -#define ST_FREEING 256 -#define ST_IBASKET 512 - -/* The idea is to keep empty inodes in a separate list, so no search - * is required as long as empty inodes exit. - * All reusable inodes occurring in the hash table with i_count==0 - * are also registered in the ringlist aged_i[level], but in LRU order. - * Used inodes with i_count>0 are kept solely in the hashtable and in - * all_i, but in no other list. - * The level is used for multilevel aging to avoid thrashing; each - * time i_count decreases to 0, the inode is inserted into the next level - * ringlist. Cache reusage is simply by taking the _last_ element from the - * lowest-level ringlist that contains inodes. - * In contrast to the old code, there isn't any O(n) search overhead now - * in iget/iput (if you make HASH_SIZE large enough). + +/* + * New inode.c implementation. + * + * This implementation has the basic premise of trying + * to be extremely low-overhead and SMP-safe, yet be + * simple enough to be "obviously correct". + * + * Famous last words. + */ + +/* + * Inode lookup is no longer as critical as it used to be: + * most of the lookups are going to be through the dcache. + */ +#define HASH_BITS 8 +#define HASH_SIZE (1UL << HASH_BITS) +#define HASH_MASK (HASH_SIZE-1) + +/* + * Each inode can be on two separate lists. One is + * the hash list of the inode, used for lookups. The + * other linked list is the "type" list: + * "in_use" - valid inode, hashed + * "dirty" - valid inode, hashed, dirty. + * "unused" - ready to be re-used. Not hashed. + * + * The two first versions also have a dirty list, allowing + * for low-overhead inode sync() operations. + */ + +static LIST_HEAD(inode_in_use); +static LIST_HEAD(inode_dirty); +static LIST_HEAD(inode_unused); +static struct list_head inode_hashtable[HASH_SIZE]; + +/* + * A simple spinlock to protect the list manipulations + */ +spinlock_t inode_lock = SPIN_LOCK_UNLOCKED; + +/* + * Statistics gathering.. Not actually done yet. */ -static struct inode * hashtable[HASH_SIZE];/* linked with i_hash_{next,prev} */ -static struct inode * all_i = NULL; /* linked with i_{next,prev} */ -static struct inode * empty_i = NULL; /* linked with i_{next,prev} */ -static struct inode * aged_i[NR_LEVELS+1]; /* linked with i_lru_{next,prev} */ -static int aged_reused[NR_LEVELS+1]; /* # removals from aged_i[level] */ -static int age_table[NR_LEVELS+1] = { /* You may tune this. */ - 1, 4, 10, 100, 1000 -}; /* after which # of uses to increase to the next level */ - -/* This is for kernel/sysctl.c */ - -/* Just aligning plain ints and arrays thereof doesn't work reliably.. */ struct { int nr_inodes; int nr_free_inodes; - int aged_count[NR_LEVELS+1]; /* # in each level */ + int dummy[10]; } inodes_stat; int max_inodes = NR_INODE; -unsigned long last_inode = 0; -void inode_init(void) +void __mark_inode_dirty(struct inode *inode) { - memset(hashtable, 0, sizeof(hashtable)); - memset(aged_i, 0, sizeof(aged_i)); - memset(aged_reused, 0, sizeof(aged_reused)); - memset(&inodes_stat, 0, sizeof(inodes_stat)); + spin_lock(&inode_lock); + list_del(&inode->i_list); + list_add(&inode->i_list, &inode_dirty); + spin_unlock(&inode_lock); } -/* Intended for short locks of the above global data structures. - * Could be replaced with spinlocks completely, since there is - * no blocking during manipulation of the static data; however the - * lock in invalidate_inodes() may last relatively long. - */ -#ifdef __SMP__ -struct semaphore vfs_sem = MUTEX; -#endif - -DEF_INSERT(all,struct inode,i_next,i_prev) -DEF_REMOVE(all,struct inode,i_next,i_prev) - -DEF_INSERT(lru,struct inode,i_lru_next,i_lru_prev) -DEF_REMOVE(lru,struct inode,i_lru_next,i_lru_prev) - -DEF_INSERT(hash,struct inode,i_hash_next,i_hash_prev) -DEF_REMOVE(hash,struct inode,i_hash_next,i_hash_prev) - -DEF_INSERT(ibasket,struct inode,i_basket_next,i_basket_prev) -DEF_REMOVE(ibasket,struct inode,i_basket_next,i_basket_prev) - -#ifdef DEBUG -extern void printpath(struct dentry * entry); -struct inode * xtst[15000]; -int xcnt = 0; - -void xcheck(char * txt, struct inode * p) +static inline void unlock_inode(struct inode *inode) { - int i; - for(i=xcnt-1; i>=0; i--) - if(xtst[i] == p) - return; - printk("Bogus inode %p in %s\n", p, txt); + clear_bit(I_LOCK, &inode->i_state); + wake_up(&inode->i_wait); } -#else -#define xcheck(t,p) /*nothing*/ -#endif -static inline struct inode * grow_inodes(void) +static void __wait_on_inode(struct inode * inode) { - struct inode * res; - struct inode * inode = res = (struct inode*)__get_free_page(GFP_KERNEL); - int size = PAGE_SIZE; - if(!inode) - return NULL; - - size -= sizeof(struct inode); - inode++; - inodes_stat.nr_inodes++; -#ifdef DEBUG -xtst[xcnt++]=res; -#endif - while(size >= sizeof(struct inode)) { -#ifdef DEBUG -xtst[xcnt++]=inode; -#endif - inodes_stat.nr_inodes++; - inodes_stat.nr_free_inodes++; - insert_all(&empty_i, inode); - inode->i_status = ST_EMPTY; - inode++; - size -= sizeof(struct inode); + struct wait_queue wait = { current, NULL }; + + add_wait_queue(&inode->i_wait, &wait); +repeat: + current->state = TASK_UNINTERRUPTIBLE; + if (test_bit(I_LOCK, &inode->i_state)) { + schedule(); + goto repeat; } - return res; + remove_wait_queue(&inode->i_wait, &wait); + current->state = TASK_RUNNING; } -static inline int hash(dev_t i_dev, unsigned long i_ino) +static inline void wait_on_inode(struct inode *inode) { - return ((int)i_ino ^ ((int)i_dev << 6)) & (HASH_SIZE-1); + if (test_bit(I_LOCK, &inode->i_state)) + __wait_on_inode(inode); } -static inline blocking void wait_io(struct inode * inode, unsigned short flags) +/* + * These are initializations that only need to be done + * once, because the fields are idempotent across use + * of the inode.. + */ +static inline void init_once(struct inode * inode) { - while(inode->i_status & flags) { - struct wait_queue wait = {current, NULL}; - inode->i_status |= ST_WAITING; - vfs_unlock(); - add_wait_queue(&inode->i_wait, &wait); - sleep_on(&inode->i_wait); - remove_wait_queue(&inode->i_wait, &wait); - vfs_lock(); - } + memset(inode, 0, sizeof(*inode)); + init_waitqueue(&inode->i_wait); + INIT_LIST_HEAD(&inode->i_dentry); + INIT_LIST_HEAD(&inode->i_hash); + sema_init(&inode->i_sem, 1); } -static inline blocking void set_io(struct inode * inode, - unsigned short waitflags, - unsigned short setflags) + +/* + * Look out! This returns with the inode lock held if + * it got an inode.. + */ +static struct inode * grow_inodes(void) { - wait_io(inode, waitflags); - inode->i_status |= setflags; - vfs_unlock(); + struct inode * inode = (struct inode *)__get_free_page(GFP_KERNEL); + + if (inode) { + int size; + struct inode * tmp; + + spin_lock(&inode_lock); + size = PAGE_SIZE - 2*sizeof(struct inode); + tmp = inode; + do { + tmp++; + init_once(tmp); + list_add(&tmp->i_list, &inode_unused); + size -= sizeof(struct inode); + } while (size >= 0); + init_once(inode); + } + return inode; } -static inline blocking int release_io(struct inode * inode, unsigned short flags) +static inline void write_inode(struct inode *inode) { - int res = 0; - vfs_lock(); - inode->i_status &= ~flags; - if(inode->i_status & ST_WAITING) { - inode->i_status &= ~ST_WAITING; - vfs_unlock(); - wake_up(&inode->i_wait); - res = 1; - } - return res; + if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode) + inode->i_sb->s_op->write_inode(inode); } -static inline blocking void _io(void (*op)(struct inode*), struct inode * inode, - unsigned short waitflags, unsigned short setflags) +static inline void sync_list(struct list_head *head, struct list_head *clean) { - /* Do nothing if the same op is already in progress. */ - if(op && !(inode->i_status & setflags)) { - set_io(inode, waitflags, setflags); - op(inode); - if(release_io(inode, setflags)) { - /* Somebody grabbed my inode from under me. */ -#ifdef DEBUG - printk("_io grab!\n"); -#endif - vfs_lock(); + struct list_head * tmp; + + while ((tmp = head->prev) != head) { + struct inode *inode = list_entry(tmp, struct inode, i_list); + list_del(tmp); + + /* + * If the inode is locked, it's already being written out. + * We have to wait for it, though. + */ + if (test_bit(I_LOCK, &inode->i_state)) { + list_add(tmp, head); + spin_unlock(&inode_lock); + __wait_on_inode(inode); + } else { + list_add(tmp, clean); + clear_bit(I_DIRTY, &inode->i_state); + set_bit(I_LOCK, &inode->i_state); + spin_unlock(&inode_lock); + write_inode(inode); + unlock_inode(inode); } - } + spin_lock(&inode_lock); + } } -blocking int _free_ibasket(struct super_block * sb) +/* + * "sync_inodes()" goes through the dirty list + * and writes them out and puts them back on + * the normal list. + */ +void sync_inodes(kdev_t dev) { - if(sb->s_ibasket) { - struct inode * delinquish = sb->s_ibasket->i_basket_prev; -#if 0 -printpath(delinquish->i_dentry); -printk(" delinquish\n"); -#endif - _clear_inode(delinquish, 0, 1); - return 1; - } - return 0; + spin_lock(&inode_lock); + sync_list(&inode_dirty, &inode_in_use); + spin_unlock(&inode_lock); } -static /*inline*/ void _put_ibasket(struct inode * inode) +/* + * This is called by the filesystem to tell us + * that the inode is no longer useful. We just + * terminate it with extreme predjudice. + */ +void clear_inode(struct inode *inode) { - struct super_block * sb = inode->i_sb; - if(!(inode->i_status & ST_IBASKET)) { - inode->i_status |= ST_IBASKET; - insert_ibasket(&sb->s_ibasket, inode); - sb->s_ibasket_count++; - if(sb->s_ibasket_count > sb->s_ibasket_max) - (void)_free_ibasket(sb); - } + truncate_inode_pages(inode, 0); + wait_on_inode(inode); + if (IS_WRITABLE(inode) && inode->i_sb && inode->i_sb->dq_op) + inode->i_sb->dq_op->drop(inode); + + inode->i_state = 0; } -blocking void _clear_inode(struct inode * inode, int external, int verbose) +#define CAN_UNUSE(inode) \ + (((inode)->i_count == 0) && \ + ((inode)->i_nrpages == 0) && \ + (!(inode)->i_state)) + +static void invalidate_list(struct list_head *head, kdev_t dev) { -xcheck("_clear_inode",inode); - if(inode->i_status & ST_IBASKET) { - struct super_block * sb = inode->i_sb; - remove_ibasket(&sb->s_ibasket, inode); - sb->s_ibasket_count--; - inode->i_status &= ~ST_IBASKET; -#if 0 -printpath(inode->i_dentry); -printk(" put_inode\n"); -#endif - _io(sb->s_op->put_inode, inode, ST_TO_PUT|ST_TO_WRITE, ST_TO_PUT); - if(inode->i_status & ST_EMPTY) - return; + struct list_head *next; + + next = head->next; + for (;;) { + struct list_head * tmp = next; + struct inode * inode; + + next = next->next; + if (tmp == head) + break; + inode = list_entry(tmp, struct inode, i_list); + if (inode->i_dev != dev) + continue; + if (!CAN_UNUSE(inode)) + continue; + list_del(&inode->i_hash); + INIT_LIST_HEAD(&inode->i_hash); + list_del(&inode->i_list); + list_add(&inode->i_list, &inode_unused); } - if(inode->i_status & ST_HASHED) - remove_hash(&hashtable[hash(inode->i_dev, inode->i_ino)], inode); - if(inode->i_status & ST_AGED) { - /* "cannot happen" when called from an fs because at least - * the caller must use it. Can happen when called from - * invalidate_inodes(). */ - if(verbose) - printk("VFS: clearing aged inode\n"); - if(atomic_read(&inode->i_count)) - printk("VFS: aged inode is in use\n"); - remove_lru(&aged_i[inode->i_level], inode); - inodes_stat.aged_count[inode->i_level]--; - } - if(!external && inode->i_status & ST_IO) { - printk("VFS: clearing inode during IO operation\n"); - } - if(!(inode->i_status & ST_EMPTY)) { - remove_all(&all_i, inode); - inode->i_status = ST_EMPTY; - while(inode->i_dentry) { - d_del(inode->i_dentry, D_NO_CLEAR_INODE); - } - if(inode->i_pages) { - vfs_unlock(); /* may block, can that be revised? */ - truncate_inode_pages(inode, 0); - vfs_lock(); - } - insert_all(&empty_i, inode); - inodes_stat.nr_free_inodes++; - } else if(external) - printk("VFS: empty inode is unnecessarily cleared multiple " - "times by an fs\n"); - else - printk("VFS: clearing empty inode\n"); - inode->i_status = ST_EMPTY; - /* The inode is not really cleared any more here, but only once - * when taken from empty_i. This saves instructions and processor - * cache pollution. - */ } -void insert_inode_hash(struct inode * inode) +void invalidate_inodes(kdev_t dev) { -xcheck("insert_inode_hash",inode); - vfs_lock(); - if(!(inode->i_status & ST_HASHED)) { - insert_hash(&hashtable[hash(inode->i_dev, inode->i_ino)], inode); - inode->i_status |= ST_HASHED; - } else - printk("VFS: trying to hash an inode again\n"); - vfs_unlock(); + spin_lock(&inode_lock); + invalidate_list(&inode_in_use, dev); + invalidate_list(&inode_dirty, dev); + spin_unlock(&inode_lock); } -blocking struct inode * _get_empty_inode(void) +/* + * This is called with the inode lock held. It just looks at the last + * inode on the in-use list, and if the inode is trivially freeable + * we just move it to the unused list. + * + * Otherwise we just move the inode to be the first inode and expect to + * get back to the problem later.. + */ +static void try_to_free_inodes(void) { - struct inode * inode; - int retry = 0; - -retry: - inode = empty_i; - if(inode) { - remove_all(&empty_i, inode); - inodes_stat.nr_free_inodes--; - } else if(inodes_stat.nr_inodes < max_inodes || retry > 2) { - inode = grow_inodes(); - } - if(!inode) { - int level; - int usable = 0; - for(level = 0; level <= NR_LEVELS; level++) - if(aged_i[level]) { - inode = aged_i[level]->i_lru_prev; - /* Here is the picking strategy, tune this */ - if(aged_reused[level] < (usable++ ? - inodes_stat.aged_count[level] : - 2)) - break; - aged_reused[level] = 0; - } - if(inode) { - if(!(inode->i_status & ST_AGED)) - printk("VFS: inode aging inconsistency\n"); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) - printk("VFS: i_count of aged inode is not zero\n"); - if(inode->i_dirt) - printk("VFS: Hey, somebody made my aged inode dirty\n"); - _clear_inode(inode, 0, 0); - goto retry; + struct list_head * tmp; + struct list_head *head = &inode_in_use; + + tmp = head->prev; + if (tmp != head) { + struct inode * inode; + + list_del(tmp); + inode = list_entry(tmp, struct inode, i_list); + if (CAN_UNUSE(inode)) { + list_del(&inode->i_hash); + INIT_LIST_HEAD(&inode->i_hash); + head = &inode_unused; } + list_add(tmp, head); } - if(!inode) { - vfs_unlock(); - schedule(); - if(retry > 10) - panic("VFS: cannot repair inode shortage"); - if(retry > 2) - printk("VFS: no free inodes\n"); - retry++; - vfs_lock(); - goto retry; - } -xcheck("get_empty_inode",inode); - memset(inode, 0, sizeof(struct inode)); - atomic_set(&inode->i_count, 1); - inode->i_nlink = 1; - sema_init(&inode->i_sem, 1); - inode->i_ino = ++last_inode; - inode->i_version = ++event; - insert_all(&all_i, inode); - return inode; } + -static inline blocking struct inode * _get_empty_inode_hashed(dev_t i_dev, - unsigned long i_ino) +static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head) { - struct inode ** base = &hashtable[hash(i_dev, i_ino)]; - struct inode * inode = *base; - if(inode) do { - if(inode->i_ino == i_ino && inode->i_dev == i_dev) { - atomic_inc(&inode->i_count); - printk("VFS: inode %lx is already in use\n", i_ino); - return inode; - } - inode = inode->i_hash_next; - } while(inode != *base); - inode = _get_empty_inode(); - inode->i_dev = i_dev; - inode->i_ino = i_ino; - insert_hash(base, inode); - inode->i_status |= ST_HASHED; + struct list_head *tmp; + struct inode * inode; + + tmp = head; + for (;;) { + tmp = tmp->next; + inode = NULL; + if (tmp == head) + break; + inode = list_entry(tmp, struct inode, i_hash); + if (inode->i_sb != sb) + continue; + if (inode->i_ino != ino) + continue; + inode->i_count++; + break; + } return inode; } -blocking struct inode * get_empty_inode_hashed(dev_t i_dev, unsigned long i_ino) +/* + * This just initializes the inode fields + * to known values before returning the inode.. + * + * i_sb, i_ino, i_count, i_state and the lists have + * been initialized elsewhere.. + */ +void clean_inode(struct inode *inode) { - struct inode * inode; - - vfs_lock(); - inode = _get_empty_inode_hashed(i_dev, i_ino); - vfs_unlock(); - return inode; + memset(&inode->u, 0, sizeof(inode->u)); + inode->i_sock = 0; + inode->i_op = NULL; + inode->i_nlink = 1; + inode->i_writecount = 0; + inode->i_size = 0; + memset(&inode->i_dquot, 0, sizeof(inode->i_dquot)); + sema_init(&inode->i_sem, 1); } -void _get_inode(struct inode * inode) +/* + * This gets called with I_LOCK held: it needs + * to read the inode and then unlock it + */ +static inline void read_inode(struct inode *inode, struct super_block *sb) { - if(inode->i_status & ST_IBASKET) { - inode->i_status &= ~ST_IBASKET; - remove_ibasket(&inode->i_sb->s_ibasket, inode); - inode->i_sb->s_ibasket_count--; - } - if(inode->i_status & ST_AGED) { - inode->i_status &= ~ST_AGED; - remove_lru(&aged_i[inode->i_level], inode); - inodes_stat.aged_count[inode->i_level]--; - aged_reused[inode->i_level]++; - if(S_ISDIR(inode->i_mode)) - /* make dirs less thrashable */ - inode->i_level = NR_LEVELS-1; - else if(inode->i_nlink > 1) - /* keep hardlinks totally separate */ - inode->i_level = NR_LEVELS; - else if(++inode->i_reuse_count >= age_table[inode->i_level] - && inode->i_level < NR_LEVELS-1) - inode->i_level++; - if(atomic_read(&inode->i_count) != 1) - printk("VFS: inode count was not zero\n"); - } else if(inode->i_status & ST_EMPTY) - printk("VFS: invalid reuse of empty inode\n"); + sb->s_op->read_inode(inode); + unlock_inode(inode); } -blocking struct inode * __iget(struct super_block * sb, - unsigned long i_ino, - int crossmntp) +struct inode * get_empty_inode(void) { - struct inode ** base; + static unsigned long last_ino = 0; struct inode * inode; - dev_t i_dev; - - if(!sb) - panic("VFS: iget with sb == NULL"); - i_dev = sb->s_dev; - if(!i_dev) - panic("VFS: sb->s_dev is NULL\n"); - base = &hashtable[hash(i_dev, i_ino)]; - vfs_lock(); - inode = *base; - if(inode) do { - if(inode->i_ino == i_ino && inode->i_dev == i_dev) { - atomic_inc(&inode->i_count); - _get_inode(inode); - - /* Allow concurrent writes/puts. This is in particular - * useful e.g. when syncing large chunks. - * I hope the i_dirty flag is everywhere set as soon - * as _any_ modifcation is made and _before_ - * giving up control, so no harm should occur if data - * is modified during writes, because it will be - * rewritten again (does a short inconsistency on the - * disk harm?) - */ - wait_io(inode, ST_TO_READ); - vfs_unlock(); - goto done; - } - inode = inode->i_hash_next; - } while(inode != *base); - inode = _get_empty_inode_hashed(i_dev, i_ino); - inode->i_sb = sb; - inode->i_flags = sb->s_flags; - if(sb->s_op && sb->s_op->read_inode) { - set_io(inode, 0, ST_TO_READ); /* do not wait at all */ - sb->s_op->read_inode(inode); - if(release_io(inode, ST_TO_READ)) - goto done; - } - vfs_unlock(); -done: - while(crossmntp && inode->i_mount) { - struct inode * tmp = inode->i_mount; - iinc(tmp); - iput(inode); - inode = tmp; + struct list_head * tmp; + + spin_lock(&inode_lock); + try_to_free_inodes(); + tmp = inode_unused.next; + if (tmp != &inode_unused) { + list_del(tmp); + inode = list_entry(tmp, struct inode, i_list); +add_new_inode: + inode->i_sb = NULL; + inode->i_ino = ++last_ino; + inode->i_count = 1; + list_add(&inode->i_list, &inode_in_use); + inode->i_state = 0; + spin_unlock(&inode_lock); + clean_inode(inode); + return inode; } -xcheck("_iget",inode); + + /* + * Warning: if this succeeded, we will now + * return with the inode lock. + */ + spin_unlock(&inode_lock); + inode = grow_inodes(); + if (inode) + goto add_new_inode; + return inode; } -blocking void __iput(struct inode * inode) +/* + * This is called with the inode lock held.. Be careful. + */ +static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head) { - struct super_block * sb; -xcheck("_iput",inode); - if(atomic_read(&inode->i_count) + inode->i_ddir_count < 0) - printk("VFS: i_count is negative\n"); - if((atomic_read(&inode->i_count) + inode->i_ddir_count) || - (inode->i_status & ST_FREEING)) { - return; - } - inode->i_status |= ST_FREEING; -#ifdef CONFIG_OMIRR - if(inode->i_status & ST_MODIFIED) { - inode->i_status &= ~ST_MODIFIED; - omirr_printall(inode, " W %ld ", CURRENT_TIME); - } -#endif - if(inode->i_pipe) { - free_page((unsigned long)PIPE_BASE(*inode)); - PIPE_BASE(*inode)= NULL; - } - if((sb = inode->i_sb)) { - if(sb->s_type && (sb->s_type->fs_flags & FS_NO_DCACHE)) { - while(inode->i_dentry) - d_del(inode->i_dentry, D_NO_CLEAR_INODE); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) - goto done; - } - if(sb->s_op) { - if(inode->i_nlink <= 0 && inode->i_dent_count && - !(inode->i_status & (ST_EMPTY|ST_IBASKET)) && - (sb->s_type->fs_flags & FS_IBASKET)) { - _put_ibasket(inode); - goto done; - } - if(!inode->i_dent_count || - (sb->s_type->fs_flags & FS_NO_DCACHE)) { - _io(sb->s_op->put_inode, inode, - ST_TO_PUT|ST_TO_WRITE, ST_TO_PUT); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) - goto done; - if(inode->i_nlink <= 0) { - if(!(inode->i_status & ST_EMPTY)) { - _clear_inode(inode, 0, 1); - } - goto done; - } - } - if(inode->i_dirt) { - inode->i_dirt = 0; - _io(sb->s_op->write_inode, inode, - ST_TO_PUT|ST_TO_WRITE, ST_TO_WRITE); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) - goto done; - } - } - if(IS_WRITABLE(inode) && sb->dq_op) { - /* can operate in parallel to other ops ? */ - _io(sb->dq_op->drop, inode, 0, ST_TO_DROP); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) - goto done; - } - } - if(inode->i_mmap) - printk("VFS: inode has mappings\n"); - if(inode->i_status & ST_AGED) { - printk("VFS: reaging inode\n"); -#if defined(DEBUG) -printpath(inode->i_dentry); -printk("\n"); -#endif - goto done; - } - if(!(inode->i_status & (ST_HASHED|ST_EMPTY))) { - _clear_inode(inode, 0, 1); - goto done; + struct inode * inode; + struct list_head * tmp = inode_unused.next; + + if (tmp != &inode_unused) { + list_del(tmp); + inode = list_entry(tmp, struct inode, i_list); +add_new_inode: + list_add(&inode->i_list, &inode_in_use); + list_add(&inode->i_hash, head); + inode->i_sb = sb; + inode->i_dev = sb->s_dev; + inode->i_ino = ino; + inode->i_flags = sb->s_flags; + inode->i_count = 1; + inode->i_state = 1 << I_LOCK; + spin_unlock(&inode_lock); + clean_inode(inode); + read_inode(inode, sb); + return inode; } - if(inode->i_status & ST_EMPTY) { - printk("VFS: aging an empty inode\n"); - goto done; + + /* + * Uhhuh.. We need to expand. Unlock for the allocation, + * but note that "grow_inodes()" will return with the + * lock held again if the allocation succeeded. + */ + spin_unlock(&inode_lock); + inode = grow_inodes(); + if (inode) { + /* We released the lock, so.. */ + struct inode * old = find_inode(sb, ino, head); + if (!old) + goto add_new_inode; + list_add(&inode->i_list, &inode_unused); + spin_unlock(&inode_lock); + wait_on_inode(old); + return old; } - insert_lru(&aged_i[inode->i_level], inode); - inodes_stat.aged_count[inode->i_level]++; - inode->i_status |= ST_AGED; -done: - inode->i_status &= ~ST_FREEING; + return inode; } -blocking void _iput(struct inode * inode) +static inline unsigned long hash(struct super_block *sb, unsigned long i_ino) { - vfs_lock(); - __iput(inode); - vfs_unlock(); + unsigned long tmp = i_ino | (unsigned long) sb; + tmp = tmp + (tmp >> HASH_BITS) + (tmp >> HASH_BITS*2); + return tmp & HASH_MASK; } -blocking void sync_inodes(kdev_t dev) +struct inode *iget(struct super_block *sb, unsigned long ino) { + struct list_head * head = inode_hashtable + hash(sb,ino); struct inode * inode; - vfs_lock(); - inode = all_i; - if(inode) do { -xcheck("sync_inodes",inode); - if(inode->i_dirt && (inode->i_dev == dev || !dev)) { - if(inode->i_sb && inode->i_sb->s_op && - !(inode->i_status & ST_FREEING)) { - inode->i_dirt = 0; - _io(inode->i_sb->s_op->write_inode, inode, - ST_IO, ST_TO_WRITE); - } - } - inode = inode->i_next; - } while(inode != all_i); - vfs_unlock(); + + spin_lock(&inode_lock); + inode = find_inode(sb, ino, head); + if (!inode) { + try_to_free_inodes(); + return get_new_inode(sb, ino, head); + } + spin_unlock(&inode_lock); + wait_on_inode(inode); + return inode; } -blocking int _check_inodes(kdev_t dev, int complain) +void insert_inode_hash(struct inode *inode) { - struct inode * inode; - int bad = 0; - - vfs_lock(); -startover: - inode = all_i; - if(inode) do { - struct inode * next; -xcheck("_check_inodes",inode); - next = inode->i_next; - if(inode->i_dev == dev) { - if(inode->i_dirt || atomic_read(&inode->i_count)) { - bad++; - } else { - _clear_inode(inode, 0, 0); - - /* _clear_inode() may recursively clear other - * inodes, probably also the next one. - */ - if(next->i_status & ST_EMPTY) - goto startover; - } - } - inode = next; - } while(inode != all_i); - vfs_unlock(); - if(complain && bad) - printk("VFS: %d inode(s) busy on removed device `%s'\n", - bad, kdevname(dev)); - return (bad == 0); + struct list_head *head = inode_hashtable + hash(inode->i_sb, inode->i_ino); + list_add(&inode->i_hash, head); } -/*inline*/ void invalidate_inodes(kdev_t dev) +void iput(struct inode *inode) { - /* Requires two passes, because of the new dcache holding - * directories with i_count > 1. - */ - (void)_check_inodes(dev, 0); - (void)_check_inodes(dev, 1); + if (inode) { + struct super_operations *op = NULL; + + if (inode->i_sb && inode->i_sb->s_op) + op = inode->i_sb->s_op; + if (op && op->put_inode) + op->put_inode(inode); + + spin_lock(&inode_lock); + if (!--inode->i_count) { + if (!inode->i_nlink) { + list_del(&inode->i_hash); + INIT_LIST_HEAD(&inode->i_hash); + list_del(&inode->i_list); + INIT_LIST_HEAD(&inode->i_list); + if (op && op->delete_inode) { + void (*delete)(struct inode *) = op->delete_inode; + spin_unlock(&inode_lock); + delete(inode); + spin_lock(&inode_lock); + } + } + if (list_empty(&inode->i_hash)) { + list_del(&inode->i_list); + list_add(&inode->i_list, &inode_unused); + } + } + spin_unlock(&inode_lock); + } } -/*inline*/ int fs_may_mount(kdev_t dev) +int bmap(struct inode * inode, int block) { - return _check_inodes(dev, 0); + if (inode->i_op && inode->i_op->bmap) + return inode->i_op->bmap(inode, block); + return 0; } -int fs_may_remount_ro(kdev_t dev) +/* + * Initialize the hash tables + */ +void inode_init(void) { - (void)dev; - return 1; /* not checked any more */ + int i; + struct list_head *head = inode_hashtable; + + i = HASH_SIZE; + do { + INIT_LIST_HEAD(head); + head++; + i--; + } while (i); } -int fs_may_umount(kdev_t dev, struct inode * mount_root) +/* + * FIXME! These need to go through the in-use inodes to + * check whether we can mount/umount/remount. + */ +int fs_may_mount(kdev_t dev) { - struct inode * inode; - vfs_lock(); - inode = all_i; - if(inode) do { -xcheck("fs_may_umount",inode); - if(inode->i_dev == dev && atomic_read(&inode->i_count)) - if(inode != mount_root || atomic_read(&inode->i_count) > - (inode->i_mount == inode ? 2 : 1)) { - vfs_unlock(); - return 0; - } - inode = inode->i_next; - } while(inode != all_i); - vfs_unlock(); return 1; } -extern struct inode_operations pipe_inode_operations; - -blocking struct inode * get_pipe_inode(void) +int fs_may_umount(struct super_block *sb, struct dentry * root) { - struct inode * inode = get_empty_inode(); - - PIPE_BASE(*inode) = (char*)__get_free_page(GFP_USER); - if(!(PIPE_BASE(*inode))) { - iput(inode); - return NULL; - } - inode->i_blksize = PAGE_SIZE; - inode->i_pipe = 1; - inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR; - atomic_inc(&inode->i_count); - inode->i_uid = current->fsuid; - inode->i_gid = current->fsgid; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_op = &pipe_inode_operations; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; - - /* I hope this does not introduce security problems. - * Please check and give me response. - */ - { - char dummyname[32]; - struct qstr dummy = { dummyname, 0 }; - struct dentry * new; - sprintf(dummyname, ".anonymous-pipe-%06lud", inode->i_ino); - dummy.len = strlen(dummyname); - vfs_lock(); - new = d_alloc(the_root, dummy.len, 0); - if(new) - d_add(new, inode, &dummy, D_BASKET); - vfs_unlock(); - } - return inode; + shrink_dcache(); + return root->d_count == 1; } -int bmap(struct inode * inode, int block) +int fs_may_remount_ro(struct super_block *sb) { - if (inode->i_op && inode->i_op->bmap) - return inode->i_op->bmap(inode, block); - return 0; + return 1; } diff --git a/fs/ioctl.c b/fs/ioctl.c index 6766506a8..11798a238 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -20,28 +20,27 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) { int error; int block; + struct inode * inode = filp->f_dentry->d_inode; switch (cmd) { case FIBMAP: - if (filp->f_inode->i_op == NULL) + if (inode->i_op == NULL) return -EBADF; - if (filp->f_inode->i_op->bmap == NULL) + if (inode->i_op->bmap == NULL) return -EINVAL; if ((error = get_user(block, (int *) arg)) != 0) return error; - block = filp->f_inode->i_op->bmap(filp->f_inode,block); + block = inode->i_op->bmap(inode,block); return put_user(block, (int *) arg); case FIGETBSZ: - if (filp->f_inode->i_sb == NULL) + if (inode->i_sb == NULL) return -EBADF; - return put_user(filp->f_inode->i_sb->s_blocksize, - (int *) arg); + return put_user(inode->i_sb->s_blocksize, (int *) arg); case FIONREAD: - return put_user(filp->f_inode->i_size - filp->f_pos, - (int *) arg); + return put_user(inode->i_size - filp->f_pos, (int *) arg); } if (filp->f_op && filp->f_op->ioctl) - return filp->f_op->ioctl(filp->f_inode, filp, cmd, arg); + return filp->f_op->ioctl(inode, filp, cmd, arg); return -ENOTTY; } @@ -91,10 +90,10 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) break; default: - if (filp->f_inode && S_ISREG(filp->f_inode->i_mode)) + if (filp->f_dentry && filp->f_dentry->d_inode && S_ISREG(filp->f_dentry->d_inode->i_mode)) error = file_ioctl(filp, cmd, arg); else if (filp->f_op && filp->f_op->ioctl) - error = filp->f_op->ioctl(filp->f_inode, filp, cmd, arg); + error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg); else error = -ENOTTY; } diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index e22c3ca3b..48321d356 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -54,6 +54,7 @@ struct inode_operations isofs_dir_inode_operations = NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ isofs_bmap, /* bmap */ @@ -61,20 +62,6 @@ struct inode_operations isofs_dir_inode_operations = NULL /* permission */ }; -static int parent_inode_number(struct inode * inode, struct iso_directory_record * de) -{ - int inode_number = inode->i_ino; - - if ((inode->i_sb->u.isofs_sb.s_firstdatazone) != inode->i_ino) - inode_number = inode->u.isofs_i.i_backlink; - - if (inode_number != -1) - return inode_number; - - /* This should never happen, but who knows. Try to be forgiving */ - return isofs_lookup_grandparent(inode, find_rock_ridge_relocation(de, inode)); -} - static int isofs_name_translate(char * old, int len, char * new) { int i, c; @@ -196,9 +183,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, /* Handle the case of the '..' directory */ if (de->name_len[0] == 1 && de->name[0] == 1) { - inode_number = parent_inode_number(inode, de); - if (inode_number == -1) - break; + inode_number = filp->f_dentry->d_parent->d_inode->i_ino; if (filldir(dirent, "..", 2, filp->f_pos, inode_number) < 0) break; filp->f_pos += de_len; diff --git a/fs/isofs/file.c b/fs/isofs/file.c index 2742283f7..d14a558a0 100644 --- a/fs/isofs/file.c +++ b/fs/isofs/file.c @@ -47,6 +47,7 @@ struct inode_operations isofs_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ isofs_bmap, /* bmap */ diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index d081a4cdd..436c22140 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -54,9 +54,10 @@ void isofs_put_super(struct super_block *sb) static struct super_operations isofs_sops = { isofs_read_inode, - NULL, /* notify_change */ NULL, /* write_inode */ NULL, /* put_inode */ + NULL, /* delete_inode */ + NULL, /* notify_change */ isofs_put_super, NULL, /* write_super */ isofs_statfs, @@ -481,12 +482,12 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, s->u.isofs_sb.s_mode = opt.mode & 0777; s->s_blocksize = opt.blocksize; s->s_blocksize_bits = blocksize_bits; - s->s_mounted = iget(s, (isonum_733(rootp->extent) + + s->s_root = d_alloc_root(iget(s, (isonum_733(rootp->extent) + isonum_711(rootp->ext_attr_length)) - << s -> u.isofs_sb.s_log_zone_size); + << s -> u.isofs_sb.s_log_zone_size), NULL); unlock_super(s); - if (!(s->s_mounted)) { + if (!(s->s_root)) { s->s_dev = 0; printk("get root inode failed\n"); MOD_DEC_USE_COUNT; @@ -504,7 +505,7 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, return NULL; } -void isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) +int isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; @@ -517,7 +518,7 @@ void isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) tmp.f_files = sb->u.isofs_sb.s_ninodes; tmp.f_ffree = 0; tmp.f_namelen = NAME_MAX; - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } int isofs_bmap(struct inode * inode,int block) @@ -663,7 +664,6 @@ void isofs_read_inode(struct inode * inode) isonum_711 (raw_inode->ext_attr_length)) << inode -> i_sb -> u.isofs_sb.s_log_zone_size; - inode->u.isofs_i.i_backlink = 0xffffffff; /* Will be used for previous directory */ switch (inode->i_sb->u.isofs_sb.s_conversion){ case 'a': inode->u.isofs_i.i_file_format = ISOFS_FILE_UNKNOWN; /* File type */ @@ -735,7 +735,6 @@ void isofs_read_inode(struct inode * inode) /* With a data error we return this information */ inode->i_mtime = inode->i_atime = inode->i_ctime = 0; inode->u.isofs_i.i_first_extent = 0; - inode->u.isofs_i.i_backlink = 0xffffffff; inode->i_size = 0; inode->i_nlink = 1; inode->i_uid = inode->i_gid = 0; diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index 155f4ae43..1c0be7fde 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -59,14 +59,13 @@ static int isofs_match(int len,const char * name, const char * compare, int dlen * entry - you'll have to do that yourself if you want to. */ static struct buffer_head * isofs_find_entry(struct inode * dir, - const char * name, int namelen, unsigned long * ino, unsigned long * ino_back) + const char * name, int namelen, unsigned long * ino) { unsigned long bufsize = ISOFS_BUFFER_SIZE(dir); unsigned char bufbits = ISOFS_BUFFER_BITS(dir); unsigned int block, i, f_pos, offset, inode_number; struct buffer_head * bh; unsigned int old_offset; - unsigned int backlink; int dlen, rrflag, match; char * dpnt; struct iso_directory_record * de; @@ -86,7 +85,6 @@ static struct buffer_head * isofs_find_entry(struct inode * dir, while (f_pos < dir->i_size) { de = (struct iso_directory_record *) (bh->b_data + offset); - backlink = dir->i_ino; inode_number = (block << bufbits) + (offset & (bufsize - 1)); /* If byte is zero, this is the end of file, or time to move to @@ -120,28 +118,6 @@ static struct buffer_head * isofs_find_entry(struct inode * dir, return 0; } - /* Handle the '.' case */ - - if (de->name[0]==0 && de->name_len[0]==1) { - inode_number = dir->i_ino; - backlink = 0; - } - - /* Handle the '..' case */ - - if (de->name[0]==1 && de->name_len[0]==1) { -#if 0 - printk("Doing .. (%d %d)", - dir->i_sb->s_firstdatazone, - dir->i_ino); -#endif - if((dir->i_sb->u.isofs_sb.s_firstdatazone) != dir->i_ino) - inode_number = dir->u.isofs_i.i_backlink; - else - inode_number = dir->i_ino; - backlink = 0; - } - dlen = de->name_len[0]; dpnt = de->name; /* Now convert the filename in the buffer to lower case */ @@ -183,16 +159,8 @@ static struct buffer_head * isofs_find_entry(struct inode * dir, inode_number = isofs_lookup_grandparent(dir, find_rock_ridge_relocation(de,dir)); - if(inode_number == -1){ - /* Should never happen */ - printk("Backlink not properly set %x %lx.\n", - isonum_733(de->extent), - dir->i_ino); - goto out; - } } *ino = inode_number; - *ino_back = backlink; return bh; } } @@ -201,62 +169,48 @@ static struct buffer_head * isofs_find_entry(struct inode * dir, return NULL; } -int isofs_lookup(struct inode * dir,const char * name, int len, - struct inode ** result) +int isofs_lookup(struct inode * dir, struct dentry * dentry) { - unsigned long ino, ino_back; + unsigned long ino; struct buffer_head * bh; char *lcname; + struct inode *inode; #ifdef DEBUG - printk("lookup: %x %d\n",dir->i_ino, len); + printk("lookup: %x %d\n",dir->i_ino, dentry->d_name.len); #endif - *result = NULL; if (!dir) return -ENOENT; - if (!S_ISDIR(dir->i_mode)) { - iput(dir); + if (!S_ISDIR(dir->i_mode)) return -ENOENT; - } /* If mounted with check=relaxed (and most likely norock), * then first convert this name to lower case. */ if (dir->i_sb->u.isofs_sb.s_name_check == 'r' && - (lcname = kmalloc(len, GFP_KERNEL)) != NULL) { + (lcname = kmalloc(dentry->d_name.len, GFP_KERNEL)) != NULL) { int i; char c; - for (i=0; i<len; i++) { - c = name[i]; + for (i=0; i<dentry->d_name.len; i++) { + c = dentry->d_name.name[i]; if (c >= 'A' && c <= 'Z') c |= 0x20; lcname[i] = c; } - bh = isofs_find_entry(dir,lcname,len, &ino, &ino_back); + bh = isofs_find_entry(dir, lcname, dentry->d_name.len, &ino); kfree(lcname); } else - bh = isofs_find_entry(dir,name,len, &ino, &ino_back); + bh = isofs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &ino); - if (!bh) { - iput(dir); - return -ENOENT; - } - brelse(bh); + inode = NULL; + if (bh) { + brelse(bh); - if (!(*result = iget(dir->i_sb,ino))) { - iput(dir); - return -EACCES; + inode = iget(dir->i_sb,ino); + if (!inode) + return -EACCES; } - - /* We need this backlink for the ".." entry unless the name that we - * are looking up traversed a mount point (in which case the inode - * may not even be on an iso9660 filesystem, and writing to - * u.isofs_i would only cause memory corruption). - */ - if (ino_back && !(*result)->i_pipe && (*result)->i_sb == dir->i_sb) - (*result)->u.isofs_i.i_backlink = ino_back; - - iput(dir); + d_add(dentry, inode); return 0; } diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 8c41d2f39..98814d220 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -172,7 +172,7 @@ reclaimer(void *ptr) /* First, reclaim all locks that have been granted previously. */ do { for (fl = file_lock_table; fl; fl = fl->fl_next) { - inode = fl->fl_file->f_inode; + inode = fl->fl_file->f_dentry->d_inode; if (inode->i_sb->s_magic == NFS_SUPER_MAGIC && nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr) && fl->fl_u.nfs_fl.state != host->h_state diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 1506a2ba6..d219de5ea 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -42,7 +42,7 @@ nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) memset(argp, 0, sizeof(*argp)); argp->cookie = nlm_cookie++; argp->state = nsm_local_state; - lock->fh = *NFS_FH(fl->fl_file->f_inode); + lock->fh = *NFS_FH(fl->fl_file->f_dentry->d_inode); lock->caller = system_utsname.nodename; lock->oh.data = req->a_owner; lock->oh.len = sprintf(req->a_owner, "%d@%s", diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index b4d74f745..69a9eeb21 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -274,8 +274,8 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, int error; dprintk("lockd: nlmsvc_lock(%04x/%ld, ty=%d, pi=%d, %ld-%ld, bl=%d)\n", - file->f_file.f_inode->i_dev, - file->f_file.f_inode->i_ino, + file->f_file.f_dentry->d_inode->i_dev, + file->f_file.f_dentry->d_inode->i_ino, lock->fl.fl_type, lock->fl.fl_pid, lock->fl.fl_start, lock->fl.fl_end, @@ -344,8 +344,8 @@ nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, struct file_lock *fl; dprintk("lockd: nlmsvc_testlock(%04x/%ld, ty=%d, %ld-%ld)\n", - file->f_file.f_inode->i_dev, - file->f_file.f_inode->i_ino, + file->f_file.f_dentry->d_inode->i_dev, + file->f_file.f_dentry->d_inode->i_ino, lock->fl.fl_type, lock->fl.fl_start, lock->fl.fl_end); @@ -375,8 +375,8 @@ nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock) int error; dprintk("lockd: nlmsvc_unlock(%04x/%ld, pi=%d, %ld-%ld)\n", - file->f_file.f_inode->i_dev, - file->f_file.f_inode->i_ino, + file->f_file.f_dentry->d_inode->i_dev, + file->f_file.f_dentry->d_inode->i_ino, lock->fl.fl_pid, lock->fl.fl_start, lock->fl.fl_end); @@ -403,8 +403,8 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock) struct nlm_block *block; dprintk("lockd: nlmsvc_cancel(%04x/%ld, pi=%d, %ld-%ld)\n", - file->f_file.f_inode->i_dev, - file->f_file.f_inode->i_ino, + file->f_file.f_dentry->d_inode->i_dev, + file->f_file.f_dentry->d_inode->i_ino, lock->fl.fl_pid, lock->fl.fl_start, lock->fl.fl_end); diff --git a/fs/locks.c b/fs/locks.c index 8ca8aa183..909c2eacb 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -297,25 +297,34 @@ int fcntl_getlk(unsigned int fd, struct flock *l) { struct flock flock; struct file *filp; + struct dentry *dentry; + struct inode *inode; struct file_lock *fl,file_lock; int error; if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd])) - return (-EBADF); + return -EBADF; if (copy_from_user(&flock, l, sizeof(flock))) - return (-EFAULT); + return -EFAULT; if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK)) - return (-EINVAL); + return -EINVAL; - if (!filp->f_inode || !posix_make_lock(filp, &file_lock, &flock)) - return (-EINVAL); + dentry = filp->f_dentry; + if (!dentry) + return -EINVAL; + + inode = dentry->d_inode; + if (!inode) + return -EINVAL; + + if (!posix_make_lock(filp, &file_lock, &flock)) + return -EINVAL; if (filp->f_op->lock) { - error = filp->f_op->lock(filp->f_inode, filp, - F_GETLK, &file_lock); + error = filp->f_op->lock(inode, filp, F_GETLK, &file_lock); if (error < 0) - return (error); + return error; fl = &file_lock; } else { fl = posix_test_lock(filp, &file_lock); @@ -344,6 +353,7 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) struct file *filp; struct file_lock file_lock; struct flock flock; + struct dentry * dentry; struct inode *inode; int error; @@ -351,10 +361,13 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) */ if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd])) - return (-EBADF); - - if (!(inode = filp->f_inode)) - return (-EINVAL); + return -EBADF; + + if (!(dentry = filp->f_dentry)) + return -EINVAL; + + if (!(inode = dentry->d_inode)) + return -EINVAL; /* Don't allow mandatory locks on files that may be memory mapped * and shared. @@ -407,7 +420,7 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) } if (filp->f_op->lock != NULL) { - error = filp->f_op->lock(filp->f_inode, filp, cmd, &file_lock); + error = filp->f_op->lock(inode, filp, cmd, &file_lock); if (error < 0) return (error); } @@ -421,12 +434,14 @@ void locks_remove_locks(struct task_struct *task, struct file *filp) { struct file_lock file_lock, *fl; struct file_lock **before; + struct inode * inode; /* For POSIX locks we free all locks on this file for the given task. * For FLOCK we only free locks on this *open* file if it is the last * close on that file. */ - before = &filp->f_inode->i_flock; + inode = filp->f_dentry->d_inode; + before = &inode->i_flock; while ((fl = *before) != NULL) { if (((fl->fl_flags & FL_POSIX) && (fl->fl_owner == task)) || @@ -436,10 +451,9 @@ void locks_remove_locks(struct task_struct *task, struct file *filp) locks_delete_lock(before, 0); if (filp->f_op->lock) { file_lock.fl_type = F_UNLCK; - filp->f_op->lock(filp->f_inode, filp, - F_SETLK, &file_lock); + filp->f_op->lock(inode, filp, F_SETLK, &file_lock); /* List may have changed: */ - before = &filp->f_inode->i_flock; + before = &inode->i_flock; } } else { before = &fl->fl_next; @@ -454,7 +468,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl) { struct file_lock *cfl; - for (cfl = filp->f_inode->i_flock; cfl; cfl = cfl->fl_next) { + for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) { if (!(cfl->fl_flags & FL_POSIX)) continue; if (posix_locks_conflict(cfl, fl)) @@ -585,7 +599,7 @@ static int posix_make_lock(struct file *filp, struct file_lock *fl, start = filp->f_pos; break; case 2: /*SEEK_END*/ - start = filp->f_inode->i_size; + start = filp->f_dentry->d_inode->i_size; break; default: return (0); @@ -612,7 +626,7 @@ static int flock_make_lock(struct file *filp, struct file_lock *fl, { memset(fl, 0, sizeof(*fl)); - if (!filp->f_inode) /* just in case */ + if (!filp->f_dentry) /* just in case */ return (0); switch (cmd & ~LOCK_NB) { @@ -750,9 +764,10 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller, struct file_lock *fl; struct file_lock *new_fl; struct file_lock **before; + struct inode * inode = filp->f_dentry->d_inode; int change = 0; - before = &filp->f_inode->i_flock; + before = &inode->i_flock; while (((fl = *before) != NULL) && (fl->fl_flags & FL_FLOCK)) { if (caller->fl_file == fl->fl_file) { if (caller->fl_type == fl->fl_type) @@ -772,7 +787,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller, if ((new_fl = locks_alloc_lock(caller)) == NULL) return (-ENOLCK); repeat: - for (fl = filp->f_inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK); + for (fl = inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK); fl = fl->fl_next) { if (!flock_locks_conflict(new_fl, fl)) continue; @@ -801,7 +816,7 @@ repeat: } goto repeat; } - locks_insert_lock(&filp->f_inode->i_flock, new_fl); + locks_insert_lock(&inode->i_flock, new_fl); return (0); } @@ -825,11 +840,12 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, struct file_lock *left = NULL; struct file_lock *right = NULL; struct file_lock **before; + struct inode * inode = filp->f_dentry->d_inode; int added = 0; if (caller->fl_type != F_UNLCK) { repeat: - for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) { + for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & FL_POSIX)) continue; if (!posix_locks_conflict(caller, fl)) @@ -852,7 +868,7 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, /* Find the first old lock with the same owner as the new lock. */ - before = &filp->f_inode->i_flock; + before = &inode->i_flock; /* First skip locks owned by other processes. */ @@ -1054,7 +1070,7 @@ static char *lock_get_status(struct file_lock *fl, int id, char *pfx) char *p = temp; struct inode *inode; - inode = fl->fl_file->f_inode; + inode = fl->fl_file->f_dentry->d_inode; p += sprintf(p, "%d:%s ", id, pfx); if (fl->fl_flags & FL_POSIX) { diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c index 81ac9b047..f47f779d5 100644 --- a/fs/minix/bitmap.c +++ b/fs/minix/bitmap.c @@ -191,8 +191,8 @@ void minix_free_inode(struct inode * inode) printk("free_inode: inode has no device\n"); return; } - if (atomic_read(&inode->i_count) != 1) { - printk("free_inode: inode has count=%d\n",atomic_read(&inode->i_count)); + if (inode->i_count != 1) { + printk("free_inode: inode has count=%d\n",inode->i_count); return; } if (inode->i_nlink) { @@ -251,12 +251,12 @@ struct inode * minix_new_inode(const struct inode * dir) iput(inode); return NULL; } - atomic_set(&inode->i_count, 1); + inode->i_count = 1; inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; - inode->i_dirt = 1; + mark_inode_dirty(inode); inode->i_ino = j; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_op = NULL; diff --git a/fs/minix/dir.c b/fs/minix/dir.c index 439005f4e..ec5113c4a 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -50,6 +50,7 @@ struct inode_operations minix_dir_inode_operations = { minix_mknod, /* mknod */ minix_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/minix/file.c b/fs/minix/file.c index 86cbca2b2..7ca7cb075 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -58,6 +58,7 @@ struct inode_operations minix_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ minix_bmap, /* bmap */ @@ -120,6 +121,6 @@ static long minix_file_write(struct inode * inode, struct file * filp, inode->i_size = pos; inode->i_mtime = inode->i_ctime = CURRENT_TIME; filp->f_pos = pos; - inode->i_dirt = 1; + mark_inode_dirty(inode); return written; } diff --git a/fs/minix/inode.c b/fs/minix/inode.c index cbd735ef1..898f56f19 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -24,10 +24,8 @@ #include <asm/uaccess.h> #include <asm/bitops.h> -void minix_put_inode(struct inode *inode) +static void minix_delete_inode(struct inode *inode) { - if (inode->i_nlink) - return; inode->i_size = 0; minix_truncate(inode); minix_free_inode(inode); @@ -77,9 +75,10 @@ void minix_put_super(struct super_block *sb) static struct super_operations minix_sops = { minix_read_inode, - NULL, minix_write_inode, - minix_put_inode, + NULL, /* put_inode */ + minix_delete_inode, + NULL, /* notify_change */ minix_put_super, minix_write_super, minix_statfs, @@ -125,15 +124,13 @@ int minix_remount (struct super_block * sb, int * flags, char * data) * it really _is_ a minix filesystem, and to check the size * of the directory entry. */ -static const char * minix_checkroot(struct super_block *s) +static const char * minix_checkroot(struct super_block *s, struct inode *dir) { - struct inode * dir; struct buffer_head *bh; struct minix_dir_entry *de; const char * errmsg; int dirsize; - dir = s->s_mounted; if (!S_ISDIR(dir->i_mode)) return "root directory is not a directory"; @@ -172,7 +169,8 @@ struct super_block *minix_read_super(struct super_block *s,void *data, int i, block; kdev_t dev = s->s_dev; const char * errmsg; - + struct inode *root_inode; + if (32 != sizeof (struct minix_inode)) panic("bad V1 i-node size"); if (64 != sizeof(struct minix2_inode)) @@ -272,8 +270,9 @@ struct super_block *minix_read_super(struct super_block *s,void *data, /* set up enough so that it can read an inode */ s->s_dev = dev; s->s_op = &minix_sops; - s->s_mounted = iget(s,MINIX_ROOT_INO); - if (!s->s_mounted) { + root_inode = iget(s,MINIX_ROOT_INO); + s->s_root = d_alloc_root(root_inode, NULL); + if (!s->s_root) { s->s_dev = 0; brelse(bh); if (!silent) @@ -282,11 +281,11 @@ struct super_block *minix_read_super(struct super_block *s,void *data, return NULL; } - errmsg = minix_checkroot(s); + errmsg = minix_checkroot(s, root_inode); if (errmsg) { if (!silent) printk("MINIX-fs: %s\n", errmsg); - iput (s->s_mounted); + d_delete(s->s_root); /* XXX Is this enough? */ s->s_dev = 0; brelse (bh); MOD_DEC_USE_COUNT; @@ -307,7 +306,7 @@ struct super_block *minix_read_super(struct super_block *s,void *data, return s; } -void minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +int minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; @@ -319,7 +318,7 @@ void minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) tmp.f_files = sb->u.minix_sb.s_ninodes; tmp.f_ffree = minix_count_free_inodes(sb); tmp.f_namelen = sb->u.minix_sb.s_namelen; - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } /* @@ -472,7 +471,7 @@ repeat: } *p = tmp; inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); return result; } @@ -585,7 +584,7 @@ repeat: } *p = tmp; inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); return result; } @@ -833,14 +832,12 @@ static struct buffer_head * V1_minix_update_inode(struct inode * inode) printk("Bad inode number on dev %s" ": %d is out of range\n", kdevname(inode->i_dev), ino); - inode->i_dirt = 0; return 0; } block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks + inode->i_sb->u.minix_sb.s_zmap_blocks + (ino-1)/MINIX_INODES_PER_BLOCK; if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) { printk("unable to read i-node block\n"); - inode->i_dirt = 0; return 0; } raw_inode = ((struct minix_inode *)bh->b_data) + @@ -855,7 +852,6 @@ static struct buffer_head * V1_minix_update_inode(struct inode * inode) raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev); else for (block = 0; block < 9; block++) raw_inode->i_zone[block] = inode->u.minix_i.u.i1_data[block]; - inode->i_dirt=0; mark_buffer_dirty(bh, 1); return bh; } @@ -874,14 +870,12 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode) printk("Bad inode number on dev %s" ": %d is out of range\n", kdevname(inode->i_dev), ino); - inode->i_dirt = 0; return 0; } block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks + inode->i_sb->u.minix_sb.s_zmap_blocks + (ino-1)/MINIX2_INODES_PER_BLOCK; if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) { printk("unable to read i-node block\n"); - inode->i_dirt = 0; return 0; } raw_inode = ((struct minix2_inode *)bh->b_data) + @@ -898,7 +892,6 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode) raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev); else for (block = 0; block < 10; block++) raw_inode->i_zone[block] = inode->u.minix_i.u.i2_data[block]; - inode->i_dirt=0; mark_buffer_dirty(bh, 1); return bh; } diff --git a/fs/minix/namei.c b/fs/minix/namei.c index b6041ad92..718d3dd07 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -47,9 +47,6 @@ static int minix_match(int len, const char * name, *offset += info->s_dirsize; if (!de->inode || len > info->s_namelen) return 0; - /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ - if (!len && (de->name[0]=='.') && (de->name[1]=='\0')) - return 1; return namecompare(len,info->s_namelen,name,de->name); } @@ -104,31 +101,22 @@ static struct buffer_head * minix_find_entry(struct inode * dir, return NULL; } -int minix_lookup(struct inode * dir,const char * name, int len, - struct inode ** result) +int minix_lookup(struct inode * dir, struct dentry *dentry) { - int ino; + struct inode * inode = NULL; struct minix_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 = minix_find_entry(dir,name,len,&de))) { - iput(dir); - return -ENOENT; - } - ino = de->inode; - brelse(bh); - if (!(*result = iget(dir->i_sb,ino))) { - iput(dir); - return -EACCES; - } - iput(dir); + bh = minix_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); + if (bh) { + unsigned long ino = le32_to_cpu(de->inode); + brelse (bh); + inode = iget(dir->i_sb, ino); + + if (!inode) + return -EACCES; + } + d_add(dentry, inode); return 0; } @@ -180,7 +168,7 @@ static int minix_add_entry(struct inode * dir, if (block*bh->b_size + offset > dir->i_size) { de->inode = 0; dir->i_size = block*bh->b_size + offset; - dir->i_dirt = 1; + mark_inode_dirty(dir); } if (de->inode) { if (namecompare(namelen, info->s_namelen, name, de->name)) { @@ -189,7 +177,7 @@ static int minix_add_entry(struct inode * dir, } } else { dir->i_mtime = dir->i_ctime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); for (i = 0; i < info->s_namelen ; i++) de->name[i] = (i < namelen) ? name[i] : 0; dir->i_version = ++event; @@ -208,42 +196,37 @@ static int minix_add_entry(struct inode * dir, return 0; } -int minix_create(struct inode * dir,const char * name, int len, int mode, - struct inode ** result) +int minix_create(struct inode * dir, struct dentry *dentry, int mode) { int error; struct inode * inode; struct buffer_head * bh; struct minix_dir_entry * de; - *result = NULL; if (!dir) return -ENOENT; inode = minix_new_inode(dir); - if (!inode) { - iput(dir); + if (!inode) return -ENOSPC; - } inode->i_op = &minix_file_inode_operations; inode->i_mode = mode; - inode->i_dirt = 1; - error = minix_add_entry(dir,name,len, &bh ,&de); + mark_inode_dirty(inode); + error = minix_add_entry(dir, dentry->d_name.name, + dentry->d_name.len, &bh ,&de); if (error) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); - iput(dir); return error; } de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); brelse(bh); - iput(dir); - *result = inode; + d_instantiate(dentry, inode); return 0; } -int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rdev) +int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int rdev) { int error; struct inode * inode; @@ -252,17 +235,15 @@ int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rd if (!dir) return -ENOENT; - bh = minix_find_entry(dir,name,len,&de); + bh = minix_find_entry(dir, dentry->d_name.name, + dentry->d_name.len, &de); if (bh) { brelse(bh); - iput(dir); return -EEXIST; } inode = minix_new_inode(dir); - if (!inode) { - iput(dir); + if (!inode) return -ENOSPC; - } inode->i_uid = current->fsuid; inode->i_mode = mode; inode->i_op = NULL; @@ -283,24 +264,22 @@ int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rd init_fifo(inode); if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = to_kdev_t(rdev); - inode->i_dirt = 1; - error = minix_add_entry(dir, name, len, &bh, &de); + mark_inode_dirty(inode); + error = minix_add_entry(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de); if (error) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); - iput(dir); return error; } de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); brelse(bh); - iput(dir); - iput(inode); + d_instantiate(dentry, inode); return 0; } -int minix_mkdir(struct inode * dir, const char * name, int len, int mode) +int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode) { int error; struct inode * inode; @@ -308,33 +287,26 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode) struct minix_dir_entry * de; struct minix_sb_info * info; - if (!dir || !dir->i_sb) { - iput(dir); + if (!dir || !dir->i_sb) return -EINVAL; - } info = &dir->i_sb->u.minix_sb; - bh = minix_find_entry(dir,name,len,&de); + bh = minix_find_entry(dir, dentry->d_name.name, + dentry->d_name.len, &de); if (bh) { brelse(bh); - iput(dir); return -EEXIST; } - if (dir->i_nlink >= MINIX_LINK_MAX) { - iput(dir); + if (dir->i_nlink >= MINIX_LINK_MAX) return -EMLINK; - } inode = minix_new_inode(dir); - if (!inode) { - iput(dir); + if (!inode) return -ENOSPC; - } inode->i_op = &minix_dir_inode_operations; inode->i_size = 2 * info->s_dirsize; dir_block = minix_bread(inode,0,1); if (!dir_block) { - iput(dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); return -ENOSPC; } @@ -350,10 +322,10 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode) inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask); if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; - inode->i_dirt = 1; - error = minix_add_entry(dir, name, len, &bh, &de); + mark_inode_dirty(inode); + error = minix_add_entry(dir, dentry->d_name.name, + dentry->d_name.len, &bh, &de); if (error) { - iput(dir); inode->i_nlink=0; iput(inode); return error; @@ -361,10 +333,9 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode) de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); dir->i_nlink++; - dir->i_dirt = 1; - iput(dir); - iput(inode); + mark_inode_dirty(dir); brelse(bh); + d_instantiate(dentry, inode); return 0; } @@ -427,7 +398,7 @@ bad_dir: return 1; } -int minix_rmdir(struct inode * dir, const char * name, int len) +int minix_rmdir(struct inode * dir, struct dentry *dentry) { int retval; struct inode * inode; @@ -435,13 +406,14 @@ int minix_rmdir(struct inode * dir, const char * name, int len) struct minix_dir_entry * de; inode = NULL; - bh = minix_find_entry(dir,name,len,&de); + bh = minix_find_entry(dir, dentry->d_name.name, + dentry->d_name.len, &de); retval = -ENOENT; if (!bh) goto end_rmdir; retval = -EPERM; - if (!(inode = iget(dir->i_sb, de->inode))) - goto end_rmdir; + inode = dentry->d_inode; + if ((dir->i_mode & S_ISVTX) && !fsuser() && current->fsuid != inode->i_uid && current->fsuid != dir->i_uid) @@ -462,7 +434,7 @@ int minix_rmdir(struct inode * dir, const char * name, int len) retval = -ENOENT; goto end_rmdir; } - if (atomic_read(&inode->i_count) > 1) { + if (inode->i_count > 1) { retval = -EBUSY; goto end_rmdir; } @@ -472,19 +444,18 @@ int minix_rmdir(struct inode * dir, const char * name, int len) dir->i_version = ++event; mark_buffer_dirty(bh, 1); inode->i_nlink=0; - inode->i_dirt=1; + mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_nlink--; - dir->i_dirt=1; + mark_inode_dirty(dir); + d_delete(dentry); retval = 0; end_rmdir: - iput(dir); - iput(inode); brelse(bh); return retval; } -int minix_unlink(struct inode * dir, const char * name, int len) +int minix_unlink(struct inode * dir, struct dentry *dentry) { int retval; struct inode * inode; @@ -494,16 +465,16 @@ int minix_unlink(struct inode * dir, const char * name, int len) repeat: retval = -ENOENT; inode = NULL; - bh = minix_find_entry(dir,name,len,&de); + bh = minix_find_entry(dir, dentry->d_name.name, + dentry->d_name.len, &de); if (!bh) goto end_unlink; - if (!(inode = iget(dir->i_sb, de->inode))) - goto end_unlink; + inode = dentry->d_inode; + retval = -EPERM; if (S_ISDIR(inode->i_mode)) goto end_unlink; if (de->inode != inode->i_ino) { - iput(inode); brelse(bh); current->counter = 0; schedule(); @@ -527,19 +498,19 @@ repeat: dir->i_version = ++event; mark_buffer_dirty(bh, 1); dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); inode->i_nlink--; inode->i_ctime = dir->i_ctime; - inode->i_dirt = 1; + mark_inode_dirty(inode); + d_delete(dentry); /* This also frees the inode */ retval = 0; end_unlink: brelse(bh); - iput(inode); - iput(dir); return retval; } -int minix_symlink(struct inode * dir, const char * name, int len, const char * symname) +int minix_symlink(struct inode * dir, struct dentry *dentry, + const char * symname) { struct minix_dir_entry * de; struct inode * inode = NULL; @@ -547,17 +518,15 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s int i; char c; - if (!(inode = minix_new_inode(dir))) { - iput(dir); + if (!(inode = minix_new_inode(dir))) return -ENOSPC; - } + inode->i_mode = S_IFLNK | 0777; inode->i_op = &minix_symlink_inode_operations; name_block = minix_bread(inode,0,1); if (!name_block) { - iput(dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); return -ENOSPC; } @@ -568,93 +537,81 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s mark_buffer_dirty(name_block, 1); brelse(name_block); inode->i_size = i; - inode->i_dirt = 1; - bh = minix_find_entry(dir,name,len,&de); + mark_inode_dirty(inode); + bh = minix_find_entry(dir, dentry->d_name.name, + dentry->d_name.len, &de); if (bh) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); brelse(bh); - iput(dir); return -EEXIST; } - i = minix_add_entry(dir, name, len, &bh, &de); + i = minix_add_entry(dir, dentry->d_name.name, + dentry->d_name.len, &bh, &de); if (i) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); - iput(dir); return i; } de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); brelse(bh); - iput(dir); - iput(inode); + d_instantiate(dentry, inode); return 0; } -int minix_link(struct inode * oldinode, struct inode * dir, const char * name, int len) +int minix_link(struct inode * inode, struct inode * dir, + struct dentry *dentry) { int error; struct minix_dir_entry * de; struct buffer_head * bh; - if (S_ISDIR(oldinode->i_mode)) { - iput(oldinode); - iput(dir); + if (S_ISDIR(inode->i_mode)) return -EPERM; - } - if (oldinode->i_nlink >= MINIX_LINK_MAX) { - iput(oldinode); - iput(dir); + + if (inode->i_nlink >= MINIX_LINK_MAX) return -EMLINK; - } - bh = minix_find_entry(dir,name,len,&de); + + bh = minix_find_entry(dir, dentry->d_name.name, + dentry->d_name.len, &de); if (bh) { brelse(bh); - iput(dir); - iput(oldinode); return -EEXIST; } - error = minix_add_entry(dir, name, len, &bh, &de); + error = minix_add_entry(dir, dentry->d_name.name, + dentry->d_name.len, &bh, &de); if (error) { - iput(dir); - iput(oldinode); + brelse(bh); return error; } - de->inode = oldinode->i_ino; + de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); brelse(bh); - iput(dir); - oldinode->i_nlink++; - oldinode->i_ctime = CURRENT_TIME; - oldinode->i_dirt = 1; - iput(oldinode); + inode->i_nlink++; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + d_instantiate(dentry, inode); return 0; } -static int subdir(struct inode * new_inode, struct inode * old_inode) +static int subdir(struct dentry * new_dentry, struct dentry * old_dentry) { - int ino; - int result; + int result = 0; - atomic_inc(&new_inode->i_count); - result = 0; for (;;) { - if (new_inode == old_inode) { - result = 1; - break; + if (new_dentry != old_dentry) { + struct dentry * parent = new_dentry->d_parent; + if (parent == new_dentry) + break; + new_dentry = parent; + continue; } - if (new_inode->i_dev != old_inode->i_dev) - break; - ino = new_inode->i_ino; - if (minix_lookup(new_inode,"..",2,&new_inode)) - break; - if (new_inode->i_ino == ino) - break; + result = 1; + break; } - iput(new_inode); return result; } @@ -671,8 +628,8 @@ static int subdir(struct inode * new_inode, struct inode * old_inode) * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ -static int do_minix_rename(struct inode * old_dir, const char * old_name, int old_len, - struct inode * new_dir, const char * new_name, int new_len) +static int do_minix_rename(struct inode * old_dir, struct dentry *old_dentry, + struct inode * new_dir, struct dentry *new_dentry) { struct inode * old_inode, * new_inode; struct buffer_head * old_bh, * new_bh, * dir_bh; @@ -686,28 +643,26 @@ try_again: brelse(old_bh); brelse(new_bh); brelse(dir_bh); - iput(old_inode); - iput(new_inode); current->counter = 0; schedule(); start_up: old_inode = new_inode = NULL; old_bh = new_bh = dir_bh = NULL; - old_bh = minix_find_entry(old_dir,old_name,old_len,&old_de); + old_bh = minix_find_entry(old_dir, old_dentry->d_name.name, + old_dentry->d_name.len, &old_de); retval = -ENOENT; if (!old_bh) goto end_rename; - old_inode = __iget(old_dir->i_sb, old_de->inode,0); /* don't cross mnt-points */ - if (!old_inode) - goto end_rename; + old_inode = old_dentry->d_inode; retval = -EPERM; if ((old_dir->i_mode & S_ISVTX) && current->fsuid != old_inode->i_uid && current->fsuid != old_dir->i_uid && !fsuser()) goto end_rename; - new_bh = minix_find_entry(new_dir,new_name,new_len,&new_de); + new_inode = new_dentry->d_inode; + new_bh = minix_find_entry(new_dir, new_dentry->d_name.name, + new_dentry->d_name.len, &new_de); if (new_bh) { - new_inode = __iget(new_dir->i_sb, new_de->inode, 0); if (!new_inode) { brelse(new_bh); new_bh = NULL; @@ -722,13 +677,13 @@ start_up: if (!S_ISDIR(old_inode->i_mode)) goto end_rename; retval = -EINVAL; - if (subdir(new_dir, old_inode)) + if (subdir(new_dentry, old_dentry)) goto end_rename; retval = -ENOTEMPTY; if (!empty_dir(new_inode)) goto end_rename; retval = -EBUSY; - if (atomic_read(&new_inode->i_count) > 1) + if (new_inode->i_count > 1) goto end_rename; } retval = -EPERM; @@ -741,7 +696,7 @@ start_up: if (new_inode && !S_ISDIR(new_inode->i_mode)) goto end_rename; retval = -EINVAL; - if (subdir(new_dir, old_inode)) + if (subdir(new_dentry, old_dentry)) goto end_rename; retval = -EIO; dir_bh = minix_bread(old_inode,0,0); @@ -754,7 +709,10 @@ start_up: goto end_rename; } if (!new_bh) { - retval = minix_add_entry(new_dir,new_name,new_len,&new_bh,&new_de); + retval = minix_add_entry(new_dir, + new_dentry->d_name.name, + new_dentry->d_name.len, + &new_bh, &new_de); if (retval) goto end_rename; } @@ -769,15 +727,15 @@ start_up: old_de->inode = 0; new_de->inode = old_inode->i_ino; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; - old_dir->i_dirt = 1; + mark_inode_dirty(old_dir); old_dir->i_version = ++event; new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); new_dir->i_version = ++event; if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); } mark_buffer_dirty(old_bh, 1); mark_buffer_dirty(new_bh, 1); @@ -785,24 +743,23 @@ start_up: PARENT_INO(dir_bh->b_data) = new_dir->i_ino; mark_buffer_dirty(dir_bh, 1); old_dir->i_nlink--; - old_dir->i_dirt = 1; + mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink--; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); } else { new_dir->i_nlink++; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); } } + /* Update the dcache */ + d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name); + d_delete(new_dentry); retval = 0; end_rename: brelse(dir_bh); brelse(old_bh); brelse(new_bh); - iput(old_inode); - iput(new_inode); - iput(old_dir); - iput(new_dir); return retval; } @@ -815,8 +772,8 @@ end_rename: * the same device that races occur: many renames can happen at once, as long * as they are on different partitions. */ -int minix_rename(struct inode * old_dir, const char * old_name, int old_len, - struct inode * new_dir, const char * new_name, int new_len) +int minix_rename(struct inode * old_dir, struct dentry *old_dentry, + struct inode * new_dir, struct dentry *new_dentry) { static struct wait_queue * wait = NULL; static int lock = 0; @@ -825,8 +782,8 @@ int minix_rename(struct inode * old_dir, const char * old_name, int old_len, while (lock) sleep_on(&wait); lock = 1; - result = do_minix_rename(old_dir, old_name, old_len, - new_dir, new_name, new_len); + result = do_minix_rename(old_dir, old_dentry, + new_dir, new_dentry); lock = 0; wake_up(&wait); return result; diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c index 92539cded..9f759ecc9 100644 --- a/fs/minix/symlink.c +++ b/fs/minix/symlink.c @@ -15,6 +15,7 @@ #include <asm/uaccess.h> static int minix_readlink(struct inode *, char *, int); +static struct dentry *minix_follow_link(struct inode *, struct dentry *); /* * symlinks can't do much... @@ -31,6 +32,7 @@ struct inode_operations minix_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ minix_readlink, /* readlink */ + minix_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -38,6 +40,21 @@ struct inode_operations minix_symlink_inode_operations = { NULL /* permission */ }; +static struct dentry * minix_follow_link(struct inode * inode, struct dentry * base) +{ + struct buffer_head * bh; + + bh = minix_bread(inode, 0, 0); + if (!bh) { + dput(base); + return ERR_PTR(-EIO); + } + UPDATE_ATIME(inode); + base = lookup_dentry(bh->b_data, base, 1); + brelse(bh); + return base; +} + static int minix_readlink(struct inode * inode, char * buffer, int buflen) { struct buffer_head * bh; @@ -47,7 +64,6 @@ static int minix_readlink(struct inode * inode, char * buffer, int buflen) if (buflen > 1023) buflen = 1023; bh = minix_bread(inode, 0, 0); - iput(inode); if (!bh) return 0; i = 0; diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c index a6d3d4b5e..298d6c155 100644 --- a/fs/minix/truncate.c +++ b/fs/minix/truncate.c @@ -58,7 +58,7 @@ repeat: continue; } *p = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); if (bh) { mark_buffer_clean(bh); brelse(bh); @@ -167,7 +167,7 @@ repeat: else { tmp = *p; *p = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); minix_free_block(inode->i_sb,tmp); } brelse(dind_bh); @@ -191,7 +191,7 @@ void V1_minix_truncate(struct inode * inode) schedule(); } inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } /* @@ -220,7 +220,7 @@ repeat: continue; } *p = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); if (bh) { mark_buffer_clean(bh); brelse(bh); @@ -329,7 +329,7 @@ repeat: else { tmp = *p; *p = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); minix_free_block(inode->i_sb,tmp); } brelse(dind_bh); @@ -374,7 +374,7 @@ repeat: else { tmp = *p; *p = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); minix_free_block(inode->i_sb,tmp); } brelse(tind_bh); @@ -402,7 +402,7 @@ static void V2_minix_truncate(struct inode * inode) schedule(); } inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } /* diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index bcf6782d0..9481a763c 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -280,7 +280,7 @@ static int msdos_create_entry(struct inode *dir, const char *name,int len, * XXX all times should be set by caller upon successful completion. */ dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); memcpy(de->name,name,MSDOS_NAME); memset(de->unused, 0, sizeof(de->unused)); de->attr = is_dir ? ATTR_DIR : ATTR_ARCH; @@ -295,7 +295,7 @@ static int msdos_create_entry(struct inode *dir, const char *name,int len, if (!*result) return -EIO; (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime = CURRENT_TIME; - (*result)->i_dirt = 1; + mark_inode_dirty(*result); return 0; } @@ -369,7 +369,7 @@ static int msdos_empty(struct inode *dir) struct buffer_head *bh; struct msdos_dir_entry *de; - if (atomic_read(&dir->i_count) > 1) + if (dir->i_count > 1) return -EBUSY; if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */ pos = 0; @@ -415,7 +415,8 @@ int msdos_rmdir(struct inode *dir,const char *name,int len) inode->i_nlink = 0; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_nlink--; - inode->i_dirt = dir->i_dirt = 1; + mark_inode_dirty(inode); + mark_inode_dirty(dir); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); res = 0; @@ -465,7 +466,7 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode) 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; + mark_inode_dirty(dot); iput(dot); if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,2,1,0,&dot)) < 0) goto mkdir_error; @@ -473,7 +474,7 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode) 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; + mark_inode_dirty(dot); MSDOS_I(inode)->i_busy = 0; iput(dot); iput(inode); @@ -519,7 +520,8 @@ static int msdos_unlinkx( inode->i_nlink = 0; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; MSDOS_I(inode)->i_busy = 1; - inode->i_dirt = dir->i_dirt = 1; + mark_inode_dirty(inode); + mark_inode_dirty(dir); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); unlink_done: @@ -580,11 +582,11 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len, } if (S_ISDIR(new_inode->i_mode)) { new_dir->i_nlink--; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); new_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, new_bh, 1); iput(new_inode); @@ -675,7 +677,7 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); new_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, new_bh, 1); } @@ -696,14 +698,14 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, } if (exists && S_ISDIR(new_inode->i_mode)) { new_dir->i_nlink--; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); } msdos_read_inode(free_inode); MSDOS_I(old_inode)->i_busy = 1; MSDOS_I(old_inode)->i_linked = free_inode; MSDOS_I(free_inode)->i_oldlink = old_inode; fat_cache_inval_inode(old_inode); - old_inode->i_dirt = 1; + mark_inode_dirty(old_inode); old_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, old_bh, 1); fat_mark_buffer_dirty(sb, free_bh, 1); @@ -711,7 +713,7 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, MSDOS_I(new_inode)->i_depend = free_inode; MSDOS_I(free_inode)->i_old = new_inode; /* Two references now exist to free_inode so increase count */ - atomic_inc(&free_inode->i_count); + free_inode->i_count++; /* free_inode is put after putting new_inode and old_inode */ iput(new_inode); fat_brelse(sb, new_bh); @@ -726,7 +728,7 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, } dotdot_de->start = MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start; - dotdot_inode->i_dirt = 1; + mark_inode_dirty(dotdot_inode); fat_mark_buffer_dirty(sb, dotdot_bh, 1); old_dir->i_nlink--; new_dir->i_nlink++; @@ -793,6 +795,7 @@ struct inode_operations msdos_dir_inode_operations = { NULL, /* mknod */ msdos_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ fat_bmap, /* bmap */ diff --git a/fs/namei.c b/fs/namei.c index 198179b98..2ec173f0d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -12,7 +12,6 @@ * lookup logic. */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -20,10 +19,7 @@ #include <linux/fcntl.h> #include <linux/stat.h> #include <linux/mm.h> -#include <linux/dalloc.h> -#include <linux/nametrans.h> #include <linux/proc_fs.h> -#include <linux/omirr.h> #include <linux/smp.h> #include <linux/smp_lock.h> @@ -37,10 +33,6 @@ #undef DEBUG /* some other debugging */ -/* local flags for __namei() */ -#define NAM_SEMLOCK 8 /* set a semlock on the last dir */ -#define NAM_TRANSCREATE 16 /* last component may be created, try "=CREATE#" suffix*/ -#define NAM_NO_TRAILSLASH 32 /* disallow trailing slashes by returning EISDIR */ #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) /* [Feb-1997 T. Schoebel-Theuer] @@ -56,7 +48,7 @@ * is done solely in the VFS level, such that <fs>_follow_link() is not * used any more and could be removed in future. As a side effect, * dir_namei(), _namei() and follow_link() are now replaced with a single - * function __namei() that can handle all the special cases of the former + * function lookup_dentry() that can handle all the special cases of the former * code. * * With the new dcache, the pathname is stored at each inode, at least as @@ -95,13 +87,13 @@ static inline char * get_page(void) char * res; down(&quicklock); res = quicklist; - if(res) { + if (res) { #ifdef DEBUG char * tmp = res; int i; for(i=0; i<quickcount; i++) tmp = *(char**)tmp; - if(tmp) + if (tmp) printk("bad quicklist %x\n", (int)tmp); #endif quicklist = *(char**)res; @@ -113,9 +105,21 @@ static inline char * get_page(void) return res; } +/* + * Kernel pointers have redundant information, so we can use a + * scheme where we can return either an error code or a dentry + * pointer with the same return value. + * + * This should be a per-architecture thing, to allow different + * error and pointer decisions. + */ +#define ERR_PTR(err) ((void *)((long)(err))) +#define PTR_ERR(ptr) ((long)(ptr)) +#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000)) + inline void putname(char * name) { - if(name) { + if (name) { down(&quicklock); *(char**)name = quicklist; quicklist = name; @@ -154,20 +158,22 @@ static inline int do_getname(const char *filename, char *page) return retval; } -int getname(const char * filename, char **result) +char * getname(const char * filename) { - char *tmp; - int retval; + char *tmp, *result; + result = ERR_PTR(-ENOMEM); tmp = get_page(); - if(!tmp) - return -ENOMEM; - retval = do_getname(filename, tmp); - if (retval < 0) - putname(tmp); - else - *result = tmp; - return retval; + if (tmp) { + int retval = do_getname(filename, tmp); + + result = tmp; + if (retval < 0) { + putname(tmp); + result = ERR_PTR(retval); + } + } + return result; } /* @@ -222,417 +228,207 @@ void put_write_access(struct inode * inode) inode->i_writecount--; } -static /*inline */ int concat(struct qstr * name, struct qstr * appendix, char * buf) +/* + * This is called when everything else fails, and we actually have + * to go to the low-level filesystem to find out what we should do.. + * + * We get the directory semaphore, and after getting that we also + * make sure that nobody added the entry to the dcache in the meantime.. + */ +static struct dentry * real_lookup(struct dentry * parent, struct qstr * name) { - int totallen = name->len; - if(name->len > MAX_TRANS_FILELEN || - appendix->len > MAX_TRANS_SUFFIX) { - return -ENAMETOOLONG; + struct dentry * result; + struct inode *dir = parent->d_inode; + + result = ERR_PTR(-ENOTDIR); + if (dir->i_op && dir->i_op->lookup) { + down(&dir->i_sem); + result = d_lookup(parent, name); + if (!result) { + int error; + result = d_alloc(parent, name); + error = dir->i_op->lookup(dir, result); + if (error) { + d_free(result); + result = ERR_PTR(error); + } + } + up(&dir->i_sem); } - memcpy(buf, name->name, name->len); - memcpy(buf + name->len, appendix->name, appendix->len); - totallen += appendix->len; - buf[totallen] = '\0'; - return totallen; + return result; } -/* Internal lookup() using the new generic dcache. - * buf must only be supplied if appendix!=NULL. - */ -static int cached_lookup(struct inode * dir, struct qstr * name, - struct qstr * appendix, char * buf, - struct qstr * res_name, struct dentry ** res_entry, - struct inode ** result) +/* Internal lookup() using the new generic dcache. */ +static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name) { - struct qstr tmp = { name->name, name->len }; - int error; - struct dentry * cached; - - *result = NULL; - if(name->len >= D_MAXLEN) - return -ENAMETOOLONG; - vfs_lock(); - cached = d_lookup(dir, name, appendix); - if(cached) { - struct inode *inode = NULL; + struct dentry * dentry = d_lookup(parent, name); - if(cached->u.d_inode && (inode = d_inode(&cached))) { - error = 0; - if(appendix && res_name) { - tmp.len = error = concat(name, appendix, buf); - tmp.name = buf; - if(error > 0) - error = 0; - } - } else { - error = -ENOENT; + if (dentry) { + if (dentry->d_revalidate) { + /* spin_unlock(&dentry_lock); */ + dentry = dentry->d_revalidate(dentry); + /* spin_lock(&dentry_lock); */ } - vfs_unlock(); - if(res_entry) - *res_entry = cached; - /* Since we are bypassing the iget() mechanism, we have to - * fabricate the act of crossing any mount points. + /* + * The parent d_count _should_ be at least 2: one for the + * dentry we found, and one for the fact that we are using + * it. */ - if(!error && inode && inode->i_mount) { - do { - struct inode *mnti = inode->i_mount; - iinc(mnti); - iput(inode); - inode = mnti; - } while(inode->i_mount); + if (parent->d_count <= 1) { + printk("lookup of %s success in %s, but parent count is %d\n", + dentry->d_name.name, parent->d_name.name, parent->d_count); } - *result = inode; - goto done; - } else - vfs_unlock(); - - if(appendix) { - tmp.len = error = concat(name, appendix, buf); - tmp.name = buf; - if(error < 0) - goto done; - } - atomic_inc(&dir->i_count); - error = dir->i_op->lookup(dir, tmp.name, tmp.len, result); - if(dir->i_dentry && tmp.len && - (!error || (error == -ENOENT && (!dir->i_sb || !dir->i_sb->s_type || - !(dir->i_sb->s_type->fs_flags & FS_NO_DCACHE))))) { - struct dentry * res; - vfs_lock(); - res = d_entry(dir->i_dentry, &tmp, error ? NULL : *result); - vfs_unlock(); - if(res_entry) - *res_entry = res; } -done: - if(res_name) { - if(error) { - res_name->name = name->name; - res_name->len = name->len; - } else { - res_name->name = tmp.name; - res_name->len = tmp.len; - } - } - return error; + return dentry; } -#ifdef CONFIG_TRANS_NAMES -/* If a normal filename is seen, try to determine whether a - * "#keyword=context#" file exists and return the new filename. - * If the name is to be created (create_mode), check whether a - * "#keyword=CREATE" name exists and optionally return the corresponding - * context name even if it didn't exist before. +/* + * "." and ".." are special - ".." especially so because it has to be able + * to know about the current root directory and parent relationships */ -static int check_suffixes(struct inode * dir, struct qstr * name, - int create_mode, char * buf, - struct qstr * res_name, struct dentry ** res_entry, - struct inode ** result) +static struct dentry * reserved_lookup(struct dentry * parent, struct qstr * name) { - struct translations * trans; - char * env; - struct qstr * suffixes; - int i; - int error = -ENOENT; - - if(!buf) - panic("buf==NULL"); - env = env_transl(); -#ifdef CONFIG_TRANS_RESTRICT - if(!env && dir->i_gid != CONFIG_TRANS_GID) { - return error; - } -#endif - trans = get_translations(env); - suffixes = create_mode ? trans->c_name : trans->name; - for(i = 0; i < trans->count; i++) { - error = cached_lookup(dir, name, &suffixes[i], - buf, res_name, res_entry, result); - if(!error) { - if(res_name && create_mode) { - /* buf == res_name->name, but is writable */ - memcpy(buf + name->len, - trans->name[i].name, - trans->name[i].len); - res_name->len = name->len + trans->name[i].len; - buf[res_name->len] = '\0'; - } + struct dentry *result = NULL; + if (name->name[0] == '.') { + switch (name->len) { + default: break; + case 2: + if (name->name[1] != '.') + break; + + if (parent != current->fs->root) + parent = parent->d_covers->d_parent; + /* fallthrough */ + case 1: + result = parent; } } - if(env) - free_page((unsigned long)trans); - return error; -} - -#endif - -/* Any operations involving reserved names at the VFS level should go here. */ -static /*inline*/ int reserved_lookup(struct inode * dir, struct qstr * name, - int create_mode, char * buf, - struct inode ** result) -{ - int error = -ENOENT; - if(name->name[0] == '.') { - if(name->len == 1) { - *result = dir; - error = 0; - } else if (name->len==2 && name->name[1] == '.') { - if (dir == current->fs->root) { - *result = dir; - error = 0; - } - else if(dir->i_dentry) { - error = 0; - *result = dir->i_dentry->d_parent->u.d_inode; - if(!*result) { - printk("dcache parent directory is lost"); - error = -ESTALE; /* random error */ - } - } - } - if(!error) - atomic_inc(&(*result)->i_count); - } - return error; + return result; } /* In difference to the former version, lookup() no longer eats the dir. */ -static /*inline*/ int lookup(struct inode * dir, struct qstr * name, int create_mode, - char * buf, struct qstr * res_name, - struct dentry ** res_entry, struct inode ** result) +static struct dentry * lookup(struct dentry * dir, struct qstr * name) { - int perm; - - *result = NULL; - perm = -ENOENT; - if (!dir) - goto done; + int err; + struct dentry * result; /* Check permissions before traversing mount-points. */ - perm = permission(dir,MAY_EXEC); - if (perm) - goto done; - perm = reserved_lookup(dir, name, create_mode, buf, result); - if(!perm) { - if(res_name) { - res_name->name = name->name; - res_name->len = name->len; - } - goto done; - } - perm = -ENOTDIR; - if (!dir->i_op || !dir->i_op->lookup) - goto done; -#ifdef CONFIG_TRANS_NAMES /* try suffixes */ - perm = check_suffixes(dir, name, 0, buf, res_name, res_entry, result); - if(perm) /* try original name */ -#endif - perm = cached_lookup(dir, name, NULL, buf, res_name, res_entry, result); -#ifdef CONFIG_TRANS_NAMES - if(perm == -ENOENT && create_mode) { /* try the =CREATE# suffix */ - struct inode * dummy; - if(!check_suffixes(dir, name, 1, buf, res_name, NULL, &dummy)) { - iput(dummy); - } + err = permission(dir->d_inode, MAY_EXEC); + result = ERR_PTR(err); + if (err) + goto done_error; + + result = reserved_lookup(dir, name); + if (result) + goto done_noerror; + + result = cached_lookup(dir, name); + if (result) + goto done_noerror; + + result = real_lookup(dir, name); + + if (!IS_ERR(result)) { +done_noerror: + result = dget(result->d_mounts); } -#endif -done: - return perm; +done_error: + return result; } -/* [8-Feb-97 T. Schoebel-Theuer] follow_link() modified for generic operation - * on the VFS layer: first call <fs>_readlink() and then open_namei(). - * All <fs>_follow_link() are not used any more and may be eliminated - * (by Linus; I refrained in order to not break other patches). - * Single exeption is procfs, where proc_follow_link() is used - * internally (and perhaps should be rewritten). - * Note: [partly obsolete] I removed parameters flag and mode, since now - * __namei() is called instead of open_namei(). In the old semantics, - * the _last_ instance of open_namei() did the real create() if O_CREAT was - * set and the name existed already in form of a symlink. This has been - * simplified now, and also the semantics when combined with O_EXCL has changed. - **************************************************************************** - * [13-Feb-97] Complete rewrite -> functionality of reading symlinks factored - * out into _read_link(). The above notes remain valid in principle. +/* + * This should check "link_count", but doesn't do that yet.. */ -static /*inline*/ int _read_link(struct inode * inode, char ** linkname, int loopcount) +static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry) { - unsigned long old_fs; - int error; + struct inode * inode = dentry->d_inode; - error = -ENOSYS; - if (!inode->i_op || !inode->i_op->readlink) - goto done; - error = -ELOOP; - if (current->link_count + loopcount > 10) - goto done; - error = -ENOMEM; - if(!*linkname && !(*linkname = get_page())) - goto done; - if (DO_UPDATE_ATIME(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } - atomic_inc(&inode->i_count); - old_fs = get_fs(); - set_fs(KERNEL_DS); - error = inode->i_op->readlink(inode, *linkname, PAGE_SIZE); - set_fs(old_fs); - if(!error) { - error = -ENOENT; /* ? or other error code ? */ - } else if(error > 0) { - (*linkname)[error] = '\0'; - error = 0; + if (inode && inode->i_op && inode->i_op->follow_link) { + struct dentry *result; + + /* This eats the base */ + result = inode->i_op->follow_link(inode, base); + base = dentry; + dentry = result; } -done: - iput(inode); - return error; + dput(base); + return dentry; } -/* [13-Feb-97 T. Schoebel-Theuer] complete rewrite: - * merged dir_name(), _namei() and follow_link() into one new routine - * that obeys all the special cases hidden in the old routines in a - * (hopefully) systematic way: - * parameter retrieve_mode is bitwise or'ed of the ST_* flags. - * if res_inode is a NULL pointer, dont try to retrieve the last component - * at all. Parameters with prefix last_ are used only if res_inode is - * non-NULL and refer to the last component of the path only. +/* + * Name resolution. + * + * This is the basic name resolution function, turning a pathname + * into the final dentry. */ -int __namei(int retrieve_mode, const char * name, struct inode * base, - char * buf, struct inode ** res_dir, struct inode ** res_inode, - struct qstr * last_name, struct dentry ** last_entry, - int * last_error) +struct dentry * lookup_dentry(const char * name, struct dentry * base, int follow_link) { - char c; - struct qstr this; - char * linkname = NULL; - char * oldlinkname = NULL; - int trail_flag = 0; - int loopcount = 0; - int error; -#ifdef DEBUG - if(last_name) { - last_name->name = "(Uninitialized)"; - last_name->len = 15; - } -#endif -again: - error = -ENOENT; - this.name = name; - if (this.name[0] == '/') { - if(base) - iput(base); - if (__prefix_namei(retrieve_mode, this.name, base, buf, - res_dir, res_inode, - last_name, last_entry, last_error) == 0) - return 0; - base = current->fs->root; - atomic_inc(&base->i_count); - this.name++; + struct dentry * dentry; + + if (*name == '/') { + if (base) + dput(base); + base = dget(current->fs->root); + do { + name++; + } while (*name == '/'); } else if (!base) { - base = current->fs->pwd; - atomic_inc(&base->i_count); + base = dget(current->fs->pwd); } + + if (!*name) + goto return_base; + + /* At this point we know we have a real path component. */ for(;;) { - struct inode * inode; - const char * tmp = this.name; int len; + unsigned long hash; + struct qstr this; + char c, follow; - for(len = 0; (c = *tmp++) && (c != '/'); len++) ; - this.len = len; - if(!c) + dentry = ERR_PTR(-ENOENT); + if (!base->d_inode) break; - while((c = *tmp) == '/') /* remove embedded/trailing slashes */ - tmp++; - if(!c) { - trail_flag = 1; - if(retrieve_mode & NAM_NO_TRAILSLASH) { - error = -EISDIR; - goto alldone; - } - break; - } -#if 0 - if(atomic_read(&base->i_count) == 0) - printk("vor lookup this=%s tmp=%s\n", this.name, tmp); -#endif - error = lookup(base, &this, 0, buf, NULL, NULL, &inode); -#if 0 - if(atomic_read(&base->i_count) == 0) - printk("nach lookup this=%s tmp=%s\n", this.name, tmp); -#endif - if (error) - goto alldone; - if(S_ISLNK(inode->i_mode)) { - error = _read_link(inode, &linkname, loopcount); - if(error) - goto alldone; - current->link_count++; - error = __namei((retrieve_mode & - ~(NAM_SEMLOCK|NAM_TRANSCREATE|NAM_NO_TRAILSLASH)) - | NAM_FOLLOW_LINK, - linkname, base, buf, - &base, &inode, NULL, NULL, NULL); - current->link_count--; - if(error) - goto alldone; - } -#if 0 - if(atomic_read(&base->i_count) == 0) - printk("this=%s tmp=%s\n", this.name, tmp); -#endif - this.name = tmp; - iput(base); - base = inode; - } - if(res_inode) { - if(retrieve_mode & NAM_SEMLOCK) - down(&base->i_sem); - error = lookup(base, &this, retrieve_mode & NAM_TRANSCREATE, - buf, last_name, last_entry, res_inode); - if(!error && S_ISLNK((*res_inode)->i_mode) && - ((retrieve_mode & NAM_FOLLOW_LINK) || - (trail_flag && (retrieve_mode & NAM_FOLLOW_TRAILSLASH)))) { - char * tmp; - - error = _read_link(*res_inode, &linkname, loopcount); - if(error) - goto lastdone; - if(retrieve_mode & NAM_SEMLOCK) - up(&base->i_sem); - /* exchange pages */ - name = tmp = linkname; - linkname = oldlinkname; oldlinkname = tmp; - loopcount++; - goto again; /* Tail recursion elimination "by hand", - * uses less dynamic memory. - */ - - /* Note that trail_flag is not reset, so it - * does not matter in a symlink chain where a - * trailing slash indicates a directory endpoint. - */ - } - if(!error && trail_flag && !S_ISDIR((*res_inode)->i_mode)) { - iput(*res_inode); - error = -ENOTDIR; - } - lastdone: - if(last_error) { - *last_error = error; - error = 0; + this.name = name; + hash = init_name_hash(); + len = 0; + c = *name; + do { + len++; name++; + hash = partial_name_hash(c, hash); + c = *name; + } while (c && (c != '/')); + + this.len = len; + this.hash = end_name_hash(hash); + + /* remove trailing slashes? */ + follow = follow_link; + if (c) { + follow |= c; + do { + c = *++name; + } while (c == '/'); } + + dentry = lookup(base, &this); + if (IS_ERR(dentry)) + break; + + if (!follow) + break; + + base = do_follow_link(base, dentry); + if (c && !IS_ERR(base)) + continue; + +return_base: + return base; } -alldone: - if(!error && res_dir) - *res_dir = base; - else - iput(base); - putname(linkname); - putname(oldlinkname); - return error; + dput(base); + return dentry; } /* @@ -641,24 +437,41 @@ alldone: * is used by most simple commands to get the inode of a specified name. * Open, link etc use their own routines, but this is enough for things * like 'chmod' etc. + * + * namei exists in two versions: namei/lnamei. The only difference is + * that namei follows links, while lnamei does not. */ +struct dentry * __namei(const char *pathname, int follow_link) +{ + char *name; + struct dentry *dentry; + + name = getname(pathname); + dentry = (struct dentry *) name; + if (!IS_ERR(name)) { + dentry = lookup_dentry(name, NULL, follow_link); + putname(name); + if (!IS_ERR(dentry)) { + if (!dentry->d_inode) { + dput(dentry); + dentry = ERR_PTR(-ENOENT); + } + } + } + return dentry; +} -/* [Feb 1997 T.Schoebel-Theuer] lnamei() completely removed; can be - * simulated when calling with retrieve_mode==NAM_FOLLOW_TRAILSLASH. - */ -int namei(int retrieve_mode, const char *pathname, struct inode **res_inode) +static inline struct inode *get_parent(struct dentry *dentry) { - int error; - char * tmp; + return dentry->d_parent->d_inode; +} - error = getname(pathname, &tmp); - if (!error) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - error = __namei(retrieve_mode, tmp, NULL, - buf, NULL, res_inode, NULL, NULL, NULL); - putname(tmp); - } - return error; +static inline struct inode *lock_parent(struct dentry *dentry) +{ + struct inode *dir = dentry->d_parent->d_inode; + + down(&dir->i_sem); + return dir; } /* @@ -674,175 +487,157 @@ int namei(int retrieve_mode, const char *pathname, struct inode **res_inode) * which is a lot more logical, and also allows the "no perm" needed * for symlinks (where the permissions are checked later). */ -int open_namei(const char * pathname, int flag, int mode, - struct inode ** res_inode, struct inode * base) +struct dentry * open_namei(const char * pathname, int flag, int mode) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - int error; - int lasterror; - struct inode * dir, * inode; - int namei_mode; + int acc_mode, error; + struct inode *inode; + struct dentry *dentry; mode &= S_IALLUGO & ~current->fs->umask; mode |= S_IFREG; - namei_mode = NAM_FOLLOW_LINK; - if(flag & O_CREAT) - namei_mode |= NAM_SEMLOCK|NAM_TRANSCREATE|NAM_NO_TRAILSLASH; - error = __namei(namei_mode, pathname, base, buf, - &dir, &inode, &last, NULL, &lasterror); - if (error) - goto exit; - error = lasterror; + dentry = lookup_dentry(pathname, NULL, 1); + if (IS_ERR(dentry)) + return dentry; + + acc_mode = ACC_MODE(flag); if (flag & O_CREAT) { - if (!error) { - if (flag & O_EXCL) { + struct inode *dir; + + dir = lock_parent(dentry); + /* + * The existence test must be done _after_ getting the directory + * semaphore - the dentry might otherwise change. + */ + if (dentry->d_inode) { + error = 0; + if (flag & O_EXCL) error = -EEXIST; - } } else if (IS_RDONLY(dir)) error = -EROFS; else if (!dir->i_op || !dir->i_op->create) error = -EACCES; - else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - ; /* error is already set! */ - else { - d_del(d_lookup(dir, &last, NULL), D_REMOVE); - atomic_inc(&dir->i_count); /* create eats the dir */ + else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) == 0) { if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - error = dir->i_op->create(dir, last.name, last.len, - mode, res_inode); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(dir->i_dentry, NULL, &last, - " c %ld %d ", CURRENT_TIME, mode); -#endif - up(&dir->i_sem); - goto exit_dir; + error = dir->i_op->create(dir, dentry, mode); + /* Don't check for write permission */ + acc_mode = 0; } up(&dir->i_sem); + if (error) + goto exit; } + + error = -ENOENT; + inode = dentry->d_inode; + if (!inode) + goto exit; + + error = -EISDIR; + if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE)) + goto exit; + + error = permission(inode,acc_mode); if (error) - goto exit_inode; + goto exit; - if (S_ISDIR(inode->i_mode) && (flag & 2)) { - error = -EISDIR; - goto exit_inode; - } - if ((error = permission(inode,ACC_MODE(flag))) != 0) { - goto exit_inode; - } + /* + * FIFO's, sockets and device files are special: they don't + * actually live on the filesystem itself, and as such you + * can write to them even if the filesystem is read-only. + */ if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { - /* - * 2-Feb-1995 Bruce Perens <Bruce@Pixar.com> - * Allow opens of Unix domain sockets and FIFOs for write on - * read-only filesystems. Their data does not live on the disk. - * - * If there was something like IS_NODEV(inode) for - * pipes and/or sockets I'd check it here. - */ flag &= ~O_TRUNC; - } - else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { - if (IS_NODEV(inode)) { - error = -EACCES; - goto exit_inode; - } + } else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { + error = -EACCES; + if (IS_NODEV(inode)) + goto exit; + flag &= ~O_TRUNC; } else { - if (IS_RDONLY(inode) && (flag & 2)) { - error = -EROFS; - goto exit_inode; - } + error = -EROFS; + if (IS_RDONLY(inode) && (flag & 2)) + goto exit; } /* * An append-only file must be opened in append mode for writing. */ - if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND))) { - error = -EPERM; - goto exit_inode; - } + error = -EPERM; + if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND))) + goto exit; + if (flag & O_TRUNC) { - if ((error = get_write_access(inode))) - goto exit_inode; + error = get_write_access(inode); + if (error) + goto exit; + /* * Refuse to truncate files with mandatory locks held on them. */ error = locks_verify_locked(inode); - if (error) - goto exit_inode; - if (inode->i_sb && inode->i_sb->dq_op) - inode->i_sb->dq_op->initialize(inode, -1); + if (!error) { + if (inode->i_sb && inode->i_sb->dq_op) + inode->i_sb->dq_op->initialize(inode, -1); - error = do_truncate(inode, 0); + error = do_truncate(inode, 0); + } put_write_access(inode); + if (error) + goto exit; } else if (flag & FMODE_WRITE) if (inode->i_sb && inode->i_sb->dq_op) inode->i_sb->dq_op->initialize(inode, -1); -exit_inode: - if(error) { - if(!lasterror) - iput(inode); - } else - *res_inode = inode; -exit_dir: - iput(dir); + + return dentry; + exit: - return error; + dput(dentry); + return ERR_PTR(error); } -int do_mknod(const char * filename, int mode, dev_t dev) +struct dentry * do_mknod(const char * filename, int mode, dev_t dev) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - int error, lasterror; - struct inode * dir; - struct inode * inode; + int error; + struct inode *dir; + struct dentry *dentry, *retval; mode &= ~current->fs->umask; - error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE|NAM_NO_TRAILSLASH, - filename, NULL, buf, - &dir, &inode, &last, NULL, &lasterror); + dentry = lookup_dentry(filename, NULL, 1); + if (IS_ERR(dentry)) + return dentry; + + dir = lock_parent(dentry); + + retval = ERR_PTR(-EEXIST); + if (dentry->d_inode) + goto exit_lock; + + retval = ERR_PTR(-EROFS); + if (IS_RDONLY(dir)) + goto exit_lock; + + error = permission(dir,MAY_WRITE | MAY_EXEC); + retval = ERR_PTR(error); if (error) - goto exit; - if(!lasterror) { - error = -EEXIST; - goto exit_inode; - } - if (!last.len) { - error = -ENOENT; - goto exit_inode; - } - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_inode; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_inode; - if (!dir->i_op || !dir->i_op->mknod) { - error = -ENOSYS; /* instead of EPERM, what does Posix say? */ - goto exit_inode; - } - atomic_inc(&dir->i_count); + goto exit_lock; + + retval = ERR_PTR(-EPERM); + if (!dir->i_op || !dir->i_op->mknod) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); - d_del(d_lookup(dir, &last, NULL), D_REMOVE); - error = dir->i_op->mknod(dir, last.name, last.len, mode, dev); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(dir->i_dentry, NULL, &last, " n %ld %d %d ", - CURRENT_TIME, mode, dev); -#endif + error = dir->i_op->mknod(dir, dentry, mode, dev); + retval = ERR_PTR(error); + if (!error) + retval = dget(dentry); + +exit_lock: up(&dir->i_sem); -exit_inode: - if(!lasterror) - iput(inode); - iput(dir); -exit: - return error; + dput(dentry); + return retval; } asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev) @@ -864,68 +659,63 @@ asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev) default: goto out; } - error = getname(filename,&tmp); - if (!error) { - error = do_mknod(tmp,mode,dev); + tmp = getname(filename); + error = PTR_ERR(tmp); + if (!IS_ERR(tmp)) { + struct dentry * dentry = do_mknod(tmp,mode,dev); putname(tmp); + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + dput(dentry); + error = 0; + } } out: unlock_kernel(); return error; } -/* [Feb-97 T. Schoebel-Theuer] remove_trailing_slashes() is now obsolete, - * its functionality is handled by observing trailing slashes in __namei(). +/* + * Look out: this function may change a normal dentry + * into a directory dentry (different size).. */ static inline int do_mkdir(const char * pathname, int mode) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - int error, lasterror; - struct inode * dir; - struct inode * inode; + int error; + struct inode *dir; + struct dentry *dentry; - mode &= 0777 & ~current->fs->umask; + dentry = lookup_dentry(pathname, NULL, 1); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto exit; + + dir = lock_parent(dentry); + + error = -EEXIST; + if (dentry->d_inode) + goto exit_lock; + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; - error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, pathname, NULL, buf, - &dir, &inode, &last, NULL, &lasterror); + error = permission(dir,MAY_WRITE | MAY_EXEC); if (error) - goto exit; - if(!lasterror) { - error = -EEXIST; - goto exit_inode; - } - if (!last.len) { - error = -ENOENT; - goto exit_inode; - } - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_inode; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_inode; - if (!dir->i_op || !dir->i_op->mkdir) { - error = -ENOSYS; /* instead of EPERM, what does Posix say? */ - goto exit_inode; - } - atomic_inc(&dir->i_count); + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->mkdir) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); - d_del(d_lookup(dir, &last, NULL), D_REMOVE); - mode &= 01777 & ~current->fs->umask; - error = dir->i_op->mkdir(dir, last.name, last.len, mode); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(dir->i_dentry, NULL, &last, " d %ld %d ", - CURRENT_TIME, mode); -#endif + mode &= 0777 & ~current->fs->umask; + error = dir->i_op->mkdir(dir, dentry, mode); + +exit_lock: up(&dir->i_sem); -exit_inode: - if(!lasterror) - iput(inode); - iput(dir); + dput(dentry); exit: return error; } @@ -936,8 +726,9 @@ asmlinkage int sys_mkdir(const char * pathname, int mode) char * tmp; lock_kernel(); - error = getname(pathname,&tmp); - if (!error) { + tmp = getname(pathname); + error = PTR_ERR(tmp); + if (!IS_ERR(tmp)) { error = do_mkdir(tmp,mode); putname(tmp); } @@ -945,124 +736,54 @@ asmlinkage int sys_mkdir(const char * pathname, int mode) return error; } -#if 0 /* We need a "deletefs", someone please write it. -DaveM */ -/* Perhaps this could be moved out into a new file. */ -static void basket_name(struct inode * dir, struct dentry * entry) -{ - char prefix[32]; - struct qstr prename = { prefix, 14 }; - struct qstr entname = { entry->d_name, entry->d_len }; - struct inode * inode; - struct dentry * old = entry; /* dummy */ - int i; - if(!entry || !(inode = d_inode(&entry))) - return; -#if 0 - if(atomic_read(&inode->i_count) > 2) { - extern void printpath(struct dentry *entry); - - printk("Caution: in use "); - if(inode->i_dentry) - printpath(inode->i_dentry); - printk(" i_nlink=%d i_count=%d i_ddir_count=%d i_dent_count=%d\n", - inode->i_nlink, atomic_read(&inode->i_count), - inode->i_ddir_count, inode->i_dent_count); - } -#endif - vfs_lock(); - for(i = 1; old; i++) { - sprintf(prefix, ".deleted-%04d.", i); - old = d_lookup(dir, &prename, &entname); - } - d_move(entry, dir, &prename, &entname); - vfs_unlock(); - iput(inode); -} -#endif - static inline int do_rmdir(const char * name) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - struct dentry * lastent = NULL; int error; - struct inode * dir; - struct inode * inode; - - /* [T.Schoebel-Theuer] I'm not sure which flags to use here. - * Try the following on different platforms: - * [0] rm -rf test test2 - * [1] ln -s test2 test - * [2] mkdir test || mkdir test2 - * [3] rmdir test && mkdir test2 - * [4] rmdir test/ - * Now the rusults: - * cmd | HP-UX | SunOS | Solaris | Old Linux | New Linux | - * ---------------------------------------------------------------- - * [2] | (OK) | EEXIST | EEXIST | EEXIST | (OK) - * [3] | ENOTDIR | ENOTDIR | ENOTDIR | ENOTDIR | ENOTDIR - * [4] | (OK) | EINVAL | ENOTDIR | ENOTDIR | (OK) - * So I implemented the HP-UX semantics. If this is not right - * for Posix compliancy, change the flags accordingly. If Posix - * let the question open, I'd suggest to stay at the new semantics. - * I'd even make case [3] work by adding 2 to the flags parameter - * if Posix tolerates that. - */ - error = __namei(NAM_FOLLOW_TRAILSLASH, name, NULL, buf, - &dir, &inode, &last, &lastent, NULL); - if (error) + struct inode *dir; + struct dentry *dentry; + + dentry = lookup_dentry(name, NULL, 0); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto exit; - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_dir; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_dir; + + dir = lock_parent(dentry); + error = -ENOENT; + if (!dentry->d_inode) + goto exit_lock; + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + error = permission(dir,MAY_WRITE | MAY_EXEC); + if (error) + goto exit_lock; + /* * A subdirectory cannot be removed from an append-only directory. */ - if (IS_APPEND(dir)) { - error = -EPERM; - goto exit_dir; - } - if (!dir->i_op || !dir->i_op->rmdir) { - error = -ENOSYS; /* was EPERM */ - goto exit_dir; - } + error = -EPERM; + if (IS_APPEND(dir)) + goto exit_lock; + /* Disallow removals of mountpoints. */ - if(inode->i_mount) { - error = -EBUSY; - goto exit_dir; - } + error = -EBUSY; + if (dentry->d_covers != dentry) + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->rmdir) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); -#if 0 - if(lastent && d_isbasket(lastent)) { - d_del(lastent, D_REMOVE); - error = 0; - goto exit_lock; - } -#endif - atomic_inc(&dir->i_count); - error = dir->i_op->rmdir(dir, last.name, last.len); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(lastent, NULL, NULL, " r %ld ", CURRENT_TIME); -#endif -#if 0 - if(!error && lastent) - basket_name(dir, lastent); + error = dir->i_op->rmdir(dir, dentry); + exit_lock: -#else - if(!error && lastent) - d_del(lastent, D_REMOVE); -#endif up(&dir->i_sem); -exit_dir: - iput(inode); - iput(dir); + dput(dentry); exit: return error; } @@ -1073,8 +794,9 @@ asmlinkage int sys_rmdir(const char * pathname) char * tmp; lock_kernel(); - error = getname(pathname,&tmp); - if (!error) { + tmp = getname(pathname); + error = PTR_ERR(tmp); + if (!IS_ERR(tmp)) { error = do_rmdir(tmp); putname(tmp); } @@ -1084,90 +806,44 @@ asmlinkage int sys_rmdir(const char * pathname) static inline int do_unlink(const char * name) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - struct dentry * lastent = NULL; int error; - struct inode * dir; - struct inode * inode; - - /* HP-UX shows a strange behaviour: - * touch y; ln -s y x; rm x/ - * this succeeds and removes the file y, not the symlink x! - * Solaris and old Linux remove the symlink instead, and - * old SunOS complains ENOTDIR. - * I chose the SunOS behaviour (by not using NAM_FOLLOW_TRAILSLASH), - * but I'm not shure whether I should. - * The current code generally prohibits using trailing slashes with - * non-directories if the name already exists, but not if - * it is to be newly created. - * Perhaps this should be further strengthened (by introducing - * an additional flag bit indicating whether trailing slashes are - * allowed) to get it as consistant as possible, but I don't know - * what Posix says. - */ - error = __namei(NAM_NO_TRAILSLASH, name, NULL, buf, - &dir, &inode, &last, &lastent, NULL); - if (error) + struct inode *dir; + struct dentry *dentry; + + dentry = lookup_dentry(name, NULL, 0); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto exit; - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_dir; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_dir; + + dir = lock_parent(dentry); + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + error = permission(dir,MAY_WRITE | MAY_EXEC); + if (error) + goto exit_lock; + /* * A file cannot be removed from an append-only directory. */ - if (IS_APPEND(dir)) { - error = -EPERM; - goto exit_dir; - } - if (!dir->i_op || !dir->i_op->unlink) { - error = -ENOSYS; /* was EPERM */ - goto exit_dir; - } + error = -EPERM; + if (IS_APPEND(dir)) + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->unlink) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); -#if 0 - if(atomic_read(&inode->i_count) > 1) { - extern void printpath(struct dentry *entry); - - printk("Fire "); - if(lastent) - printpath(lastent); - printk(" i_nlink=%d i_count=%d i_ddir_count=%d i_dent_count=%d\n", - inode->i_nlink, atomic_read(&inode->i_count), - inode->i_ddir_count, inode->i_dent_count); - } -#endif -#if 0 - if(lastent && d_isbasket(lastent)) { - d_del(lastent, D_REMOVE); - error = 0; - goto exit_lock; - } -#endif - atomic_inc(&dir->i_count); - error = dir->i_op->unlink(dir, last.name, last.len); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(lastent, NULL, NULL, " u %ld ", CURRENT_TIME); -#endif -#if 0 - if(!error && lastent) - basket_name(dir, lastent); + error = dir->i_op->unlink(dir, dentry); + exit_lock: -#else - if(!error && lastent) - d_del(lastent, D_REMOVE); -#endif up(&dir->i_sem); -exit_dir: - iput(inode); - iput(dir); + dput(dentry); exit: return error; } @@ -1178,8 +854,9 @@ asmlinkage int sys_unlink(const char * pathname) char * tmp; lock_kernel(); - error = getname(pathname,&tmp); - if (!error) { + tmp = getname(pathname); + error = PTR_ERR(tmp); + if (!IS_ERR(tmp)) { error = do_unlink(tmp); putname(tmp); } @@ -1189,62 +866,41 @@ asmlinkage int sys_unlink(const char * pathname) static inline int do_symlink(const char * oldname, const char * newname) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - int error, lasterror; - struct inode * dir; - struct inode * inode; - - /* The following works on HP-UX and Solaris, by producing - * a symlink chain: - * rm -rf ? ; mkdir z ; ln -s z y ; ln -s y x/ - * Under old SunOS, the following occurs: - * ln: x/: No such file or directory - * Under old Linux, very strange things occur: - * ln: cannot create symbolic link `x//y' to `y': No such file or directory - * This is very probably a bug, but may be caused by the ln program - * when checking for a directory target. - * - * I'm not shure whether to add NAM_NO_TRAILSLASH to inhibit trailing - * slashes in the target generally. - */ - error = __namei(NAM_TRANSCREATE, newname, NULL, buf, - &dir, &inode, &last, NULL, &lasterror); - if (error) + int error; + struct inode *dir; + struct dentry *dentry; + + dentry = lookup_dentry(newname, NULL, 0); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto exit; - if(!lasterror) { - iput(inode); - error = -EEXIST; - goto exit_dir; - } - if (!last.len) { - error = -ENOENT; - goto exit_dir; - } - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_dir; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_dir; - if (!dir->i_op || !dir->i_op->symlink) { - error = -ENOSYS; /* was EPERM */ - goto exit_dir; - } - atomic_inc(&dir->i_count); + + error = -EEXIST; + if (dentry->d_inode) + goto exit; + + dir = lock_parent(dentry); + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + error = permission(dir,MAY_WRITE | MAY_EXEC); + if (error) + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->symlink) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); - d_del(d_lookup(dir, &last, NULL), D_REMOVE); - error = dir->i_op->symlink(dir, last.name, last.len, oldname); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(dir->i_dentry, NULL, &last, - " s %ld %s\0", CURRENT_TIME, oldname); -#endif + error = dir->i_op->symlink(dir, dentry, oldname); + +exit_lock: up(&dir->i_sem); -exit_dir: - iput(dir); + dput(dentry); exit: return error; } @@ -1252,13 +908,16 @@ exit: asmlinkage int sys_symlink(const char * oldname, const char * newname) { int error; - char * from, * to; + char * from; lock_kernel(); - error = getname(oldname,&from); - if (!error) { - error = getname(newname,&to); - if (!error) { + from = getname(oldname); + error = PTR_ERR(from); + if (!IS_ERR(from)) { + char * to; + to = getname(newname); + error = PTR_ERR(to); + if (!IS_ERR(to)) { error = do_symlink(from,to); putname(to); } @@ -1270,73 +929,63 @@ asmlinkage int sys_symlink(const char * oldname, const char * newname) static inline int do_link(const char * oldname, const char * newname) { - char oldbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - char newbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr oldlast; - struct qstr newlast; - struct dentry * oldent = NULL; - struct inode * oldinode; - struct inode * newinode; - struct inode * newdir; - int error, lasterror; - - error = __namei(NAM_FOLLOW_LINK|NAM_NO_TRAILSLASH, - oldname, NULL, oldbuf, - NULL, &oldinode, &oldlast, &oldent, NULL); - if (error) + struct dentry *old_dentry, *new_dentry; + struct inode *dir, *inode; + int error; + + old_dentry = lookup_dentry(oldname, NULL, 1); + error = PTR_ERR(old_dentry); + if (IS_ERR(old_dentry)) goto exit; - error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, newname, NULL, newbuf, - &newdir, &newinode, &newlast, NULL, &lasterror); + new_dentry = lookup_dentry(newname, NULL, 1); + error = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto exit_old; + + dir = lock_parent(new_dentry); + + error = -ENOENT; + inode = old_dentry->d_inode; + if (!inode) + goto exit_lock; + + error = -EEXIST; + if (new_dentry->d_inode) + goto exit_lock; + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + error = -EXDEV; + if (dir->i_dev != inode->i_dev) + goto exit_lock; + + error = permission(dir, MAY_WRITE | MAY_EXEC); if (error) - goto old_exit; - if(!lasterror) { - iput(newinode); - error = -EEXIST; - goto new_exit; - } - if (!newlast.len) { - error = -EPERM; - goto new_exit; - } - if (IS_RDONLY(newdir)) { - error = -EROFS; - goto new_exit; - } - if (newdir->i_dev != oldinode->i_dev) { - error = -EXDEV; - goto new_exit; - } - if ((error = permission(newdir,MAY_WRITE | MAY_EXEC)) != 0) - goto new_exit; + goto exit_lock; + /* * A link to an append-only or immutable file cannot be created. */ - if (IS_APPEND(oldinode) || IS_IMMUTABLE(oldinode)) { - error = -EPERM; - goto new_exit; - } - if (!newdir->i_op || !newdir->i_op->link) { - error = -ENOSYS; /* was EPERM */ - goto new_exit; - } - atomic_inc(&oldinode->i_count); - atomic_inc(&newdir->i_count); - if (newdir->i_sb && newdir->i_sb->dq_op) - newdir->i_sb->dq_op->initialize(newdir, -1); - down(&newdir->i_sem); - d_del(d_lookup(newdir, &newlast, NULL), D_REMOVE); - error = newdir->i_op->link(oldinode, newdir, newlast.name, newlast.len); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(oldent, newdir->i_dentry, &newlast, - " l %ld ", CURRENT_TIME); -#endif - up(&newdir->i_sem); -new_exit: - iput(newdir); -old_exit: - iput(oldinode); + error = -EPERM; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->link) + goto exit_lock; + + if (dir->i_sb && dir->i_sb->dq_op) + dir->i_sb->dq_op->initialize(dir, -1); + error = dir->i_op->link(inode, dir, new_dentry); + +exit_lock: + up(&dir->i_sem); + dput(new_dentry); +exit_old: + dput(old_dentry); exit: return error; } @@ -1344,13 +993,16 @@ exit: asmlinkage int sys_link(const char * oldname, const char * newname) { int error; - char * from, * to; + char * from; lock_kernel(); - error = getname(oldname,&from); - if (!error) { - error = getname(newname,&to); - if (!error) { + from = getname(oldname); + error = PTR_ERR(from); + if (!IS_ERR(from)) { + char * to; + to = getname(newname); + error = PTR_ERR(to); + if (!IS_ERR(to)) { error = do_link(from,to); putname(to); } @@ -1360,105 +1012,111 @@ asmlinkage int sys_link(const char * oldname, const char * newname) return error; } -static inline int do_rename(const char * oldname, const char * newname) +/* + * Whee.. Deadlock country. Happily there is only one VFS + * operation that does this.. + */ +static inline void double_down(struct semaphore *s1, struct semaphore *s2) { - char oldbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr oldlast; - char newbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr newlast; - struct dentry * oldent = NULL; - struct inode * olddir, * newdir; - struct inode * oldinode, * newinode; - int error, newlasterror; - - error = __namei(NAM_FOLLOW_TRAILSLASH, oldname, NULL, oldbuf, - &olddir, &oldinode, &oldlast, &oldent, NULL); - if (error) - goto exit; - if ((error = permission(olddir,MAY_WRITE | MAY_EXEC)) != 0) - goto old_exit; - if (!oldlast.len || (oldlast.name[0] == '.' && - (oldlast.len == 1 || (oldlast.name[1] == '.' && - oldlast.len == 2)))) { - error = -EPERM; - goto old_exit; + if ((unsigned long) s1 < (unsigned long) s2) { + down(s1); + down(s2); + } else if (s1 == s2) { + down(s1); + atomic_dec(&s1->count); + } else { + down(s2); + down(s1); } - /* Disallow moves of mountpoints. */ - if(oldinode->i_mount) { - error = -EBUSY; - goto old_exit; +} + +static inline int is_reserved(struct dentry *dentry) +{ + if (dentry->d_name.name[0] == '.') { + switch (dentry->d_name.len) { + case 2: + if (dentry->d_name.name[1] != '.') + break; + /* fallthrough */ + case 1: + return 1; + } } + return 0; +} + +static inline int do_rename(const char * oldname, const char * newname) +{ + int error; + struct inode * old_dir, * new_dir; + struct dentry * old_dentry, *new_dentry; + + old_dentry = lookup_dentry(oldname, NULL, 0); + + error = PTR_ERR(old_dentry); + if (IS_ERR(old_dentry)) + goto exit; + + new_dentry = lookup_dentry(newname, NULL, 0); + + error = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto exit_old; + + new_dir = get_parent(new_dentry); + old_dir = get_parent(old_dentry); + + double_down(&new_dir->i_sem, &old_dir->i_sem); - error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, newname, NULL, newbuf, - &newdir, &newinode, &newlast, NULL, &newlasterror); + error = -ENOENT; + if (!old_dentry->d_inode) + goto exit_lock; + + error = permission(old_dir,MAY_WRITE | MAY_EXEC); if (error) - goto old_exit; - if ((error = permission(newdir,MAY_WRITE | MAY_EXEC)) != 0) - goto new_exit; - if (!newlast.len || (newlast.name[0] == '.' && - (newlast.len == 1 || (newlast.name[1] == '.' && - newlast.len == 2)))) { - error = -EPERM; - goto new_exit; - } - if (newdir->i_dev != olddir->i_dev) { - error = -EXDEV; - goto new_exit; - } - if (IS_RDONLY(newdir) || IS_RDONLY(olddir)) { - error = -EROFS; - goto new_exit; - } + goto exit_lock; + error = permission(new_dir,MAY_WRITE | MAY_EXEC); + if (error) + goto exit_lock; + + error = -EPERM; + if (is_reserved(new_dentry) || is_reserved(old_dentry)) + goto exit_lock; + + /* Disallow moves of mountpoints. */ + error = -EBUSY; + if (old_dentry->d_covers != old_dentry) + goto exit_lock; + + error = -EXDEV; + if (new_dir->i_dev != old_dir->i_dev) + goto exit_lock; + + error = -EROFS; + if (IS_RDONLY(new_dir) || IS_RDONLY(old_dir)) + goto exit_lock; + /* * A file cannot be removed from an append-only directory. */ - if (IS_APPEND(olddir)) { - error = -EPERM; - goto new_exit; - } - if (!olddir->i_op || !olddir->i_op->rename) { - error = -ENOSYS; /* was EPERM */ - goto new_exit; - } -#ifdef CONFIG_TRANS_NAMES - /* if oldname has been translated, but newname not (and - * has not already a suffix), take over the suffix from oldname. - */ - if(oldlast.name == oldbuf && newlast.name != newbuf && - newlast.name[newlast.len-1] != '#') { - int i = oldlast.len - 2; - while (i > 0 && oldlast.name[i] != '#') - i--; - memcpy(newbuf, newlast.name, newlast.len); - memcpy(newbuf+newlast.len, oldlast.name+i, oldlast.len - i); - newlast.len += oldlast.len - i; - newlast.name = newbuf; - } -#endif - atomic_inc(&olddir->i_count); - atomic_inc(&newdir->i_count); - if (newdir->i_sb && newdir->i_sb->dq_op) - newdir->i_sb->dq_op->initialize(newdir, -1); - down(&newdir->i_sem); - error = olddir->i_op->rename(olddir, oldlast.name, oldlast.len, - newdir, newlast.name, newlast.len); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(oldent, newdir->i_dentry, &newlast, - " m %ld ", CURRENT_TIME); -#endif - if(!error) { - d_del(d_lookup(newdir, &newlast, NULL), D_REMOVE); - d_move(d_lookup(olddir, &oldlast, NULL), newdir, &newlast, NULL); - } - up(&newdir->i_sem); -new_exit: - if(!newlasterror) - iput(newinode); - iput(newdir); -old_exit: - iput(oldinode); - iput(olddir); + error = -EPERM; + if (IS_APPEND(old_dir)) + goto exit_lock; + + error = -EPERM; + if (!old_dir->i_op || !old_dir->i_op->rename) + goto exit_lock; + + if (new_dir->i_sb && new_dir->i_sb->dq_op) + new_dir->i_sb->dq_op->initialize(new_dir, -1); + error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); + +exit_lock: + up(&new_dir->i_sem); + up(&old_dir->i_sem); + dput(new_dentry); +exit_old: + dput(old_dentry); exit: return error; } @@ -1466,13 +1124,16 @@ exit: asmlinkage int sys_rename(const char * oldname, const char * newname) { int error; - char * from, * to; + char * from; lock_kernel(); - error = getname(oldname,&from); - if (!error) { - error = getname(newname,&to); - if (!error) { + from = getname(oldname); + error = PTR_ERR(from); + if (!IS_ERR(from)) { + char * to; + to = getname(newname); + error = PTR_ERR(to); + if (!IS_ERR(to)) { error = do_rename(from,to); putname(to); } diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 72ca3e6dd..83309f3a6 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -408,7 +408,7 @@ int ncp_current_malloced; static struct file_system_type ncp_fs_type = { "ncpfs", - FS_NO_DCACHE, + 0 /* FS_NO_DCACHE doesn't work correctly */, ncp_read_super, NULL }; diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index 8e814d153..937f40cec 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -132,8 +132,8 @@ int ncp_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma) inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); vma->vm_ops = &ncp_file_mmap; return 0; } diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 71835c255..b10331c6a 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -42,16 +42,15 @@ struct nfs_dirent { static int nfs_dir_open(struct inode * inode, struct file * file); static long nfs_dir_read(struct inode *, struct file *, char *, unsigned long); static int nfs_readdir(struct inode *, struct file *, void *, filldir_t); -static int nfs_lookup(struct inode *, const char *, int, struct inode **); -static int nfs_create(struct inode *, const char *, int, int, struct inode **); -static int nfs_mkdir(struct inode *, const char *, int, int); -static int nfs_rmdir(struct inode *, const char *, int); -static int nfs_unlink(struct inode *, const char *, int); -static int nfs_symlink(struct inode *, const char *, int, const char *); -static int nfs_link(struct inode *, struct inode *, const char *, int); -static int nfs_mknod(struct inode *, const char *, int, int, int); -static int nfs_rename(struct inode *, const char *, int, - struct inode *, const char *, int); +static int nfs_lookup(struct inode *, struct dentry *); +static int nfs_create(struct inode *, struct dentry *, int); +static int nfs_mkdir(struct inode *, struct dentry *, int); +static int nfs_rmdir(struct inode *, struct dentry *); +static int nfs_unlink(struct inode *, struct dentry *); +static int nfs_symlink(struct inode *, struct dentry *, const char *); +static int nfs_link(struct inode *, struct inode *, struct dentry *); +static int nfs_mknod(struct inode *, struct dentry *, int, int); +static int nfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); static struct file_operations nfs_dir_operations = { NULL, /* lseek - default */ @@ -78,6 +77,7 @@ struct inode_operations nfs_dir_inode_operations = { nfs_mknod, /* mknod */ nfs_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -328,488 +328,267 @@ nfs_free_dircache(void) } -/* - * Lookup caching is a big win for performance but this is just - * a trial to see how well it works on a small scale. - * For example, bash does a lookup on ".." 13 times for each path - * element when running pwd. Yes, hard to believe but true. - * Try pwd in a filesystem mounted with noac. - * - * It trades a little cpu time and memory for a lot of network bandwidth. - * Since the cache is not hashed yet, it is a good idea not to make it too - * large because every lookup looks through the entire cache even - * though most of them will fail. - * - * FIXME: The lookup cache should also cache failed lookups. This can - * be a considerable win on diskless clients. - */ - -static struct nfs_lookup_cache_entry { - kdev_t dev; - ino_t inode; - char filename[NFS_MAXNAMLEN + 1]; - struct nfs_fh fhandle; - struct nfs_fattr fattr; - unsigned long expiration_date; -} nfs_lookup_cache[NFS_LOOKUP_CACHE_SIZE]; - -static struct nfs_lookup_cache_entry *nfs_lookup_cache_index(struct inode *dir, - const char *filename) -{ - struct nfs_lookup_cache_entry *entry; - int i; - - for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) { - entry = nfs_lookup_cache + i; - if (entry->dev == dir->i_dev - && entry->inode == dir->i_ino - && !strncmp(filename, entry->filename, NFS_MAXNAMLEN)) - return entry; - } - return NULL; -} - -static int nfs_lookup_cache_lookup(struct inode *dir, const char *filename, - struct nfs_fh *fhandle, - struct nfs_fattr *fattr) -{ - static int nfs_lookup_cache_in_use = 0; - - struct nfs_lookup_cache_entry *entry; - - dfprintk(LOOKUPCACHE, "NFS: lookup_cache_lookup(%x/%ld, %s)\n", - dir->i_dev, dir->i_ino, filename); - if (!nfs_lookup_cache_in_use) { - memset(nfs_lookup_cache, 0, sizeof(nfs_lookup_cache)); - nfs_lookup_cache_in_use = 1; - } - if ((entry = nfs_lookup_cache_index(dir, filename))) { - if (jiffies > entry->expiration_date) { - entry->dev = 0; - return 0; - } - *fhandle = entry->fhandle; - *fattr = entry->fattr; - return 1; - } - return 0; -} - -static void nfs_lookup_cache_add(struct inode *dir, const char *filename, - struct nfs_fh *fhandle, - struct nfs_fattr *fattr) -{ - static int nfs_lookup_cache_pos = 0; - struct nfs_lookup_cache_entry *entry; - - dfprintk(LOOKUPCACHE, "NFS: lookup_cache_add(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, filename); - - /* compensate for bug in SGI NFS server */ - if (fattr->size == -1 || fattr->uid == -1 || fattr->gid == -1 - || fattr->atime.seconds == -1 || fattr->mtime.seconds == -1) - return; - if (!(entry = nfs_lookup_cache_index(dir, filename))) { - entry = nfs_lookup_cache + nfs_lookup_cache_pos++; - if (nfs_lookup_cache_pos == NFS_LOOKUP_CACHE_SIZE) - nfs_lookup_cache_pos = 0; - } - - entry->dev = dir->i_dev; - entry->inode = dir->i_ino; - strcpy(entry->filename, filename); - entry->fhandle = *fhandle; - entry->fattr = *fattr; - entry->expiration_date = jiffies + (S_ISDIR(fattr->mode) - ? NFS_SERVER(dir)->acdirmin : NFS_SERVER(dir)->acregmin); -} - -static void nfs_lookup_cache_remove(struct inode *dir, struct inode *inode, - const char *filename) -{ - struct nfs_lookup_cache_entry *entry; - kdev_t dev; - ino_t fileid; - int i; - - if (inode) { - dev = inode->i_dev; - fileid = inode->i_ino; - } - else if ((entry = nfs_lookup_cache_index(dir, filename))) { - dev = entry->dev; - fileid = entry->fattr.fileid; - } - else - return; - - dfprintk(LOOKUPCACHE, "NFS: lookup_cache_remove(%x/%ld)\n", - dev, (long)fileid); - - for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) { - entry = nfs_lookup_cache + i; - if (entry->dev == dev && entry->fattr.fileid == fileid) - entry->dev = 0; - } -} - -static void nfs_lookup_cache_refresh(struct inode *file, - struct nfs_fattr *fattr) -{ - struct nfs_lookup_cache_entry *entry; - kdev_t dev = file->i_dev; - int fileid = file->i_ino; - int i; - - for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) { - entry = nfs_lookup_cache + i; - if (entry->dev == dev && entry->fattr.fileid == fileid) - entry->fattr = *fattr; - } -} - -static int nfs_lookup(struct inode *dir, const char *__name, int len, - struct inode **result) +static int nfs_lookup(struct inode *dir, struct dentry * dentry) { + struct inode *inode; struct nfs_fh fhandle; struct nfs_fattr fattr; - char name[len > NFS_MAXNAMLEN? 1 : len+1]; + int len = dentry->d_name.len; int error; dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n", - dir->i_dev, dir->i_ino, len, __name); + dir->i_dev, dir->i_ino, len, dentry->d_name.name); - *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_lookup: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } - memcpy(name,__name,len); - name[len] = '\0'; - if (len == 0 || (len == 1 && name[0] == '.')) { /* cheat for "" and "." */ - *result = dir; - return 0; - } - if ((NFS_SERVER(dir)->flags & NFS_MOUNT_NOAC) - || !nfs_lookup_cache_lookup(dir, name, &fhandle, &fattr)) { - if ((error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), - name, &fhandle, &fattr))) { - iput(dir); - return error; - } - nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - } - if (!(*result = nfs_fhget(dir->i_sb, &fhandle, &fattr))) { - iput(dir); - return -EACCES; - } - iput(dir); + + error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, &fhandle, &fattr); + + inode = NULL; + if (!error) { + error = -ENOENT; + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) + return -EACCES; + } else if (error != -ENOENT) + return error; + + d_add(dentry, inode); return 0; } -static int nfs_create(struct inode *dir, const char *name, int len, int mode, - struct inode **result) +static int nfs_create(struct inode *dir, struct dentry * dentry, int mode) { struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; + struct inode *inode; int error; dfprintk(VFS, "NFS: create(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); - *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_create: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } + sattr.mode = mode; sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; - if ((error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), - name, &sattr, &fhandle, &fattr))) { - iput(dir); + error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), + dentry->d_name.name, &sattr, &fhandle, &fattr); + + if (error) return error; - } - if (!(*result = nfs_fhget(dir->i_sb, &fhandle, &fattr))) { - iput(dir); + + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) return -EACCES; - } - nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - nfs_invalidate_dircache(dir); - iput(dir); + + d_instantiate(dentry, inode); return 0; } -static int nfs_mknod(struct inode *dir, const char *name, int len, - int mode, int rdev) +static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) { struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; + struct inode *inode; int error; dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_mknod: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } + sattr.mode = mode; - sattr.uid = sattr.gid = (unsigned) -1; + sattr.uid = sattr.gid = sattr.size = (unsigned) -1; if (S_ISCHR(mode) || S_ISBLK(mode)) sattr.size = rdev; /* get out your barf bag */ - else - sattr.size = (unsigned) -1; + sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), - name, &sattr, &fhandle, &fattr); - if (!error) - nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - nfs_invalidate_dircache(dir); - iput(dir); - return error; + dentry->d_name.name, &sattr, &fhandle, &fattr); + + if (error) + return error; + + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) + return -EACCES; + + d_instantiate(dentry, inode); + return 0; } -static int nfs_mkdir(struct inode *dir, const char *name, int len, int mode) +static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; + struct inode * inode; int error; dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_mkdir: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } + sattr.mode = mode; sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; + error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir), - name, &sattr, &fhandle, &fattr); - if (!error) - nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - nfs_invalidate_dircache(dir); - iput(dir); - return error; + dentry->d_name.name, &sattr, &fhandle, &fattr); + + if (error) + return error; + + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) + return -EACCES; + + d_instantiate(dentry, inode); + return 0; } -static int nfs_rmdir(struct inode *dir, const char *name, int len) +static int nfs_rmdir(struct inode *dir, struct dentry *dentry) { int error; dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_rmdir: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); - return -ENAMETOOLONG; - } - error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), name); - if (!error) - nfs_lookup_cache_remove(dir, NULL, name); - nfs_invalidate_dircache(dir); - iput(dir); - return error; -} - -static int nfs_sillyrename(struct inode *dir, const char *name, int len) -{ - struct inode *inode; - char silly[16]; - int slen, ret; - - atomic_inc(&dir->i_count); - if (nfs_lookup(dir, name, len, &inode) < 0) - return -EIO; /* arbitrary */ - - if (atomic_read(&inode->i_count) == 1) { - iput(inode); - return -EIO; - } - if (NFS_RENAMED_DIR(inode)) { - iput(NFS_RENAMED_DIR(inode)); - NFS_RENAMED_DIR(inode) = NULL; - iput(inode); - return -EIO; - } - slen = sprintf(silly, ".nfs%ld", inode->i_ino); - if (len == slen && !strncmp(name, silly, len)) { - iput(inode); - return -EIO; /* DWIM */ - } + if (dentry->d_name.len > NFS_MAXNAMLEN) + return -ENAMETOOLONG; - dfprintk(VFS, "NFS: sillyrename(%x/%ld, %s)\n", - dir->i_dev, dir->i_ino, name); + error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name); + if (error) + return error; - ret = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dir), name, - NFS_FH(dir), silly); - if (ret >= 0) { - nfs_lookup_cache_remove(dir, NULL, name); - nfs_lookup_cache_remove(dir, NULL, silly); - NFS_RENAMED_DIR(inode) = dir; - atomic_inc(&dir->i_count); - } - nfs_invalidate_dircache(dir); - iput(inode); - return ret; + d_delete(dentry); + return 0; } /* - * When releasing the inode, finally remove any unlinked but open files. - * Note that we have to clear the set of pending signals temporarily; - * otherwise the RPC call will fail. + * We should do silly-rename here, but I'm too lazy to fix + * up the directory entry implications of it.. */ -void nfs_sillyrename_cleanup(struct inode *inode) -{ - unsigned long oldsig; - struct inode *dir = NFS_RENAMED_DIR(inode); - char silly[14]; - int error, slen; - - dfprintk(VFS, "NFS: sillyrename cleanup(%x/%ld)\n", - inode->i_dev, inode->i_ino); - - oldsig = current->signal; - current->signal = 0; - - slen = sprintf(silly, ".nfs%ld", inode->i_ino); - error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), silly); - if (error < 0) - printk("NFS: silly_rename cleanup failed (err %d)\n", -error); - - nfs_lookup_cache_remove(dir, NULL, silly); - nfs_invalidate_dircache(dir); - NFS_RENAMED_DIR(inode) = NULL; - iput(dir); - - current->signal |= oldsig; -} - -static int nfs_unlink(struct inode *dir, const char *name, int len) +static int nfs_unlink(struct inode *dir, struct dentry *dentry) { int error; dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_unlink: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } - if ((error = nfs_sillyrename(dir, name, len)) < 0) { - error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), name); - if (!error) - nfs_lookup_cache_remove(dir, NULL, name); - } - nfs_invalidate_dircache(dir); - iput(dir); - return error; + + error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name); + if (error) + return error; + + d_delete(dentry); + return 0; } -static int nfs_symlink(struct inode *dir, const char *name, int len, - const char *symname) +static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct nfs_sattr sattr; + struct nfs_fattr fattr; + struct nfs_fh fhandle; + struct inode * inode; int error; dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n", - dir->i_dev, dir->i_ino, name, symname); + dir->i_dev, dir->i_ino, dentry->d_name.name, symname); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_symlink: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } - if (strlen(symname) > NFS_MAXPATHLEN) { - iput(dir); + + if (strlen(symname) > NFS_MAXPATHLEN) return -ENAMETOOLONG; - } + sattr.mode = S_IFLNK | S_IRWXUGO; /* SunOS 4.1.2 crashes without this! */ sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; + error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir), - name, symname, &sattr); - nfs_invalidate_dircache(dir); - iput(dir); - return error; + dentry->d_name.name, symname, &sattr); + + if (error) + return error; + + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) + return -EACCES; + + d_instantiate(dentry, inode); + return 0; } -static int nfs_link(struct inode *oldinode, struct inode *dir, - const char *name, int len) +static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry) { int error; dfprintk(VFS, "NFS: link(%x/%ld -> %x/%ld, %s)\n", - oldinode->i_dev, oldinode->i_ino, - dir->i_dev, dir->i_ino, name); + inode->i_dev, inode->i_ino, + dir->i_dev, dir->i_ino, dentry->d_name.name); - if (!oldinode) { - printk("nfs_link: old inode is NULL\n"); - iput(oldinode); - iput(dir); - return -ENOENT; - } if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_link: dir is NULL or not a directory\n"); - iput(oldinode); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(oldinode); - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } - error = nfs_proc_link(NFS_SERVER(oldinode), NFS_FH(oldinode), - NFS_FH(dir), name); - if (!error) { - nfs_lookup_cache_remove(dir, oldinode, NULL); - NFS_READTIME(oldinode) = 0; /* force getattr */ - } - nfs_invalidate_dircache(dir); - iput(oldinode); - iput(dir); - return error; + + error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode), + NFS_FH(dir), dentry->d_name.name); + + if (error) + return error; + + inode->i_count++; + d_instantiate(dentry, inode); + return 0; } /* @@ -821,45 +600,39 @@ static int nfs_link(struct inode *oldinode, struct inode *dir, * rename the old file using the silly_rename stuff. This way, the original * file in old_dir will go away when the last process iput()s the inode. */ -static int nfs_rename(struct inode *old_dir, const char *old_name, int old_len, - struct inode *new_dir, const char *new_name, int new_len) +static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) { int error; dfprintk(VFS, "NFS: rename(%x/%ld, %s -> %x/%ld, %s)\n", - old_dir->i_dev, old_dir->i_ino, old_name, - new_dir->i_dev, new_dir->i_ino, new_name); + old_dir->i_dev, old_dir->i_ino, old_dentry->d_name.name, + new_dir->i_dev, new_dir->i_ino, new_dentry->d_name.name); if (!old_dir || !S_ISDIR(old_dir->i_mode)) { printk("nfs_rename: old inode is NULL or not a directory\n"); - iput(old_dir); - iput(new_dir); return -ENOENT; } + if (!new_dir || !S_ISDIR(new_dir->i_mode)) { printk("nfs_rename: new inode is NULL or not a directory\n"); - iput(old_dir); - iput(new_dir); return -ENOENT; } - if (old_len > NFS_MAXNAMLEN || new_len > NFS_MAXNAMLEN) { - iput(old_dir); - iput(new_dir); + + if (old_dentry->d_name.len > NFS_MAXNAMLEN || new_dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } error = nfs_proc_rename(NFS_SERVER(old_dir), - NFS_FH(old_dir), old_name, - NFS_FH(new_dir), new_name); - if (!error) { - nfs_lookup_cache_remove(old_dir, NULL, old_name); - nfs_lookup_cache_remove(new_dir, NULL, new_name); - } - nfs_invalidate_dircache(old_dir); - nfs_invalidate_dircache(new_dir); - iput(old_dir); - iput(new_dir); - return error; + NFS_FH(old_dir), old_dentry->d_name.name, + NFS_FH(new_dir), new_dentry->d_name.name); + + if (error) + return error; + + /* Update the dcache */ + d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name); + d_delete(new_dentry); + return 0; } /* @@ -873,8 +646,7 @@ void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) int was_empty; dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n", - inode->i_dev, inode->i_ino, - atomic_read(&inode->i_count)); + inode->i_dev, inode->i_ino, inode->i_count); if (!inode || !fattr) { printk("nfs_refresh_inode: inode or fattr is NULL\n"); @@ -925,6 +697,4 @@ void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) init_fifo(inode); } else inode->i_op = NULL; - nfs_lookup_cache_refresh(inode, fattr); } - diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 56540bbdc..eb4735a6d 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -69,6 +69,7 @@ struct inode_operations nfs_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ nfs_readpage, /* readpage */ nfs_writepage, /* writepage */ NULL, /* bmap */ @@ -142,7 +143,7 @@ nfs_file_write(struct inode *inode, struct file *file, int result; dfprintk(VFS, "nfs: write(%x/%ld (%d), %lu@%lu)\n", - inode->i_dev, inode->i_ino, atomic_read(&inode->i_count), + inode->i_dev, inode->i_ino, inode->i_count, count, (unsigned long) file->f_pos); if (!inode) { @@ -179,11 +180,11 @@ nfs_lock(struct inode *inode, struct file *filp, int cmd, struct file_lock *fl) int status; dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%ld:%ld)\n", - filp->f_inode->i_dev, filp->f_inode->i_ino, + filp->f_dentry->d_inode->i_dev, filp->f_dentry->d_inode->i_ino, fl->fl_type, fl->fl_flags, fl->fl_start, fl->fl_end); - if (!(inode = filp->f_inode)) + if (!(inode = filp->f_dentry->d_inode)) return -EINVAL; /* No mandatory locks over NFS */ diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 5ab9600e9..cf52a0d56 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -36,15 +36,17 @@ static int nfs_notify_change(struct inode *, struct iattr *); static void nfs_put_inode(struct inode *); +static void nfs_delete_inode(struct inode *); static void nfs_put_super(struct super_block *); static void nfs_read_inode(struct inode *); -static void nfs_statfs(struct super_block *, struct statfs *, int bufsiz); +static int nfs_statfs(struct super_block *, struct statfs *, int bufsiz); static struct super_operations nfs_sops = { nfs_read_inode, /* read inode */ - nfs_notify_change, /* notify change */ NULL, /* write inode */ nfs_put_inode, /* put inode */ + nfs_delete_inode, /* delete inode */ + nfs_notify_change, /* notify change */ nfs_put_super, /* put superblock */ NULL, /* write superblock */ nfs_statfs, /* stat filesystem */ @@ -73,11 +75,16 @@ static void nfs_put_inode(struct inode * inode) { dprintk("NFS: put_inode(%x/%ld)\n", inode->i_dev, inode->i_ino); +} - if (NFS_RENAMED_DIR(inode)) - nfs_sillyrename_cleanup(inode); - if (inode->i_pipe) - clear_inode(inode); +/* + * This should do any silly-rename cleanups once we + * get silly-renaming working again.. + */ +static void +nfs_delete_inode(struct inode * inode) +{ + dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino); } void @@ -230,7 +237,8 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) /* Unlock super block and try to get root fh attributes */ unlock_super(sb); - if ((sb->s_mounted = nfs_fhget(sb, &data->root, NULL)) != NULL) { + sb->s_root = d_alloc_root(nfs_fhget(sb, &data->root, NULL), NULL); + if (sb->s_root != NULL) { /* We're airborne */ if (!(server->flags & NFS_MOUNT_NONLM)) lockd_up(); @@ -250,7 +258,7 @@ failure: return NULL; } -static void +static int nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { int error; @@ -271,7 +279,7 @@ nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) tmp.f_files = 0; tmp.f_ffree = 0; tmp.f_namelen = NAME_MAX; - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } /* @@ -317,7 +325,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle, } dprintk("NFS: fhget(%x/%ld ct=%d)\n", inode->i_dev, inode->i_ino, - atomic_read(&inode->i_count)); + inode->i_count); return inode; } @@ -364,7 +372,6 @@ nfs_notify_change(struct inode *inode, struct iattr *attr) nfs_truncate_dirty_pages(inode, sattr.size); nfs_refresh_inode(inode, &fattr); } - inode->i_dirt = 0; return error; } @@ -435,7 +442,7 @@ done: */ static struct file_system_type nfs_fs_type = { "nfs", - FS_NO_DCACHE, + 0 /* FS_NO_DCACHE - this doesn't work right now*/, nfs_read_super, NULL }; diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index add3309f3..51cca7c48 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -1,5 +1,5 @@ /* - * $Id: nfsroot.c,v 1.37 1997/06/04 08:28:10 davem Exp $ + * $Id: nfsroot.c,v 1.3 1997/06/17 13:26:56 ralf Exp $ * * Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de> * @@ -78,7 +78,6 @@ #include <asm/param.h> #include <linux/utsname.h> -#include <linux/nametrans.h> #include <linux/in.h> #include <linux/if.h> #include <linux/inet.h> @@ -833,9 +832,6 @@ __initfunc(static void root_do_bootp_ext(u8 *ext)) root_bootp_string(nfs_path, ext+1, *ext, NFS_MAXPATHLEN); break; } -#ifdef CONFIG_TRANS_NAMES - translations_dirty = 1; -#endif } @@ -1258,9 +1254,6 @@ __initfunc(static void root_nfs_addrs(char *addrs)) system_utsname.domainname[0] = '\0'; user_dev_name[0] = '\0'; bootp_flag = rarp_flag = 1; -#ifdef CONFIG_TRANS_NAMES - translations_dirty = 1; -#endif /* The following is just a shortcut for automatic IP configuration */ if (!strcmp(addrs, "bootp")) { @@ -1306,9 +1299,6 @@ __initfunc(static void root_nfs_addrs(char *addrs)) } strncpy(system_utsname.nodename, ip, __NEW_UTS_LEN); system_utsname.nodename[__NEW_UTS_LEN] = '\0'; -#ifdef CONFIG_TRANS_NAMES - translations_dirty = 1; -#endif break; case 5: strncpy(user_dev_name, ip, IFNAMSIZ); @@ -1342,9 +1332,6 @@ __initfunc(static int root_nfs_setup(void)) if (!system_utsname.nodename[0]) { strncpy(system_utsname.nodename, in_ntoa(myaddr), __NEW_UTS_LEN); system_utsname.nodename[__NEW_UTS_LEN] = '\0'; -#ifdef CONFIG_TRANS_NAMES - translations_dirty = 1; -#endif } /* Set the correct netmask */ diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 2c3b59036..4ce61f731 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -150,7 +150,6 @@ nfs_readpage_result(struct rpc_task *task) fail++; dprintk("NFS: %d successful reads, %d failures\n", succ, fail); } - iput(req->ra_inode); clear_bit(PG_locked, &page->flags); wake_up(&page->wait); @@ -188,7 +187,6 @@ nfs_readpage_async(struct inode *inode, struct page *page) nfs_readpage_result, req); if (result >= 0) { - atomic_inc(&inode->i_count); atomic_inc(&page->count); return 0; } diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index a22f96239..3d545f7d8 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -19,6 +19,7 @@ #include <asm/uaccess.h> static int nfs_readlink(struct inode *, char *, int); +static struct dentry *nfs_follow_link(struct inode *, struct dentry *); /* * symlinks can't do much... @@ -35,6 +36,7 @@ struct inode_operations nfs_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ nfs_readlink, /* readlink */ + nfs_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -55,7 +57,6 @@ static int nfs_readlink(struct inode *inode, char *buffer, int buflen) buflen = NFS_MAXPATHLEN; error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem, &res, &len, buflen); - iput(inode); if (! error) { copy_to_user(buffer, res, len); put_user('\0', buffer + len); @@ -64,3 +65,36 @@ static int nfs_readlink(struct inode *inode, char *buffer, int buflen) kfree(mem); return error; } + +static struct dentry * nfs_follow_link(struct inode * inode, struct dentry *base) +{ + int error; + unsigned int len; + char *res; + void *mem; + char *path; + + dfprintk(VFS, "nfs: follow_link(%x/%ld)\n", inode->i_dev, inode->i_ino); + + error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem, + &res, &len, NFS_MAXPATHLEN); + + if (error) { + dput(base); + kfree(mem); + return ERR_PTR(error); + } + path = kmalloc(len + 1, GFP_KERNEL); + if (!path) { + dput(base); + kfree(mem); + return ERR_PTR(-ENOMEM); + } + memcpy(path, res, len); + path[len] = 0; + kfree(mem); + + base = lookup_dentry(path, base, 1); + kfree(path); + return base; +} diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f27d083e4..9241c679e 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -338,7 +338,7 @@ create_write_request(struct inode *inode, struct page *page, wreq->wb_page = page; wreq->wb_offset = offset; wreq->wb_bytes = bytes; - atomic_inc(&inode->i_count); + atomic_inc(&page->count); append_write_request(&NFS_WRITEBACK(inode), wreq); @@ -695,7 +695,6 @@ nfs_check_error(struct inode *inode) status = req->wb_task.tk_status; remove_write_request(&nfs_failed_requests, req); - iput(req->wb_inode); kfree(req); return status; } @@ -788,7 +787,6 @@ nfs_wback_result(struct rpc_task *task) dprintk("NFS: %4d saving write failure code\n", task->tk_pid); append_write_request(&nfs_failed_requests, req); - atomic_inc(&inode->i_count); } clear_bit(PG_uptodate, &page->flags); } else if (!WB_CANCELLED(req)) { @@ -818,6 +816,5 @@ nfs_wback_result(struct rpc_task *task) kfree(req); free_page(page_address(page)); - iput(inode); nr_write_requests--; } diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index a3b29313a..c83150b5f 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -331,7 +331,7 @@ exp_rootfh(struct svc_client *clp, dev_t dev, ino_t ino, struct knfs_fh *f) if (!(exp = exp_get(clp, dev, ino))) return -EPERM; - atomic_inc(&exp->ex_inode->i_count); + exp->ex_inode->i_count++; fh_compose(&fh, exp, exp->ex_inode); memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh)); fh_put(&fh); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index a68fca997..b6fdb460d 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -120,13 +120,13 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, dotdot = (len == 2 && name[0] == '.' && name[1] == '.'); if (dotdot) { if (dirp == current->fs->root) { - atomic_inc(&dirp->i_count); + dirp->i_count++; *resfh = *fhp; return 0; } if (dirp->i_dev == exp->ex_dev && dirp->i_ino == exp->ex_ino) { - atomic_inc(&dirp->i_count); + dirp->i_count++; *resfh = *fhp; return 0; } @@ -144,12 +144,12 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, if (perm != 0) return perm; if (!len) { - atomic_inc(&dirp->i_count); + dirp->i_count++; *resfh = *fhp; return 0; } - atomic_inc(&dirp->i_count); /* lookup eats the dirp inode */ + dirp->i_count++; /* lookup eats the dirp inode */ err = dirp->i_op->lookup(dirp, name, len, &inode); if (err) @@ -162,7 +162,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, if (!dotdot && (sb = inode->i_sb) && (inode == sb->s_mounted)) { iput(inode); inode = sb->s_covered; - atomic_inc(&inode->i_count); + inode->i_count++; } fh_compose(resfh, exp, inode); @@ -291,7 +291,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, } } - atomic_inc(&inode->i_count); + inode->i_count++; return 0; } @@ -304,7 +304,7 @@ nfsd_close(struct file *filp) struct inode *inode; inode = filp->f_inode; - if (!atomic_read(&inode->i_count)) + if (!inode->i_count) printk(KERN_WARNING "nfsd: inode count == 0!\n"); if (filp->f_op && filp->f_op->release) filp->f_op->release(inode, filp); @@ -533,7 +533,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, fh_lock(fhp); /* lock directory */ dirp = fhp->fh_inode; - atomic_inc(&dirp->i_count); /* dirop eats the inode */ + dirp->i_count++; /* dirop eats the inode */ switch (type) { case S_IFREG: @@ -568,7 +568,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, * If the VFS call doesn't return the inode, look it up now. */ if (inode == NULL) { - atomic_inc(&dirp->i_count); + dirp->i_count++; err = dirp->i_op->lookup(dirp, fname, flen, &inode); if (err < 0) return -nfserrno(err); /* Huh?! */ @@ -643,7 +643,7 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp) if (!inode->i_op || !inode->i_op->readlink) return nfserr_io; - atomic_inc(&inode->i_count); + inode->i_count++; oldfs = get_fs(); set_fs(KERNEL_DS); err = inode->i_op->readlink(inode, buf, *lenp); set_fs(oldfs); @@ -680,7 +680,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, return nfserr_perm; fh_lock(fhp); /* lock inode */ - atomic_inc(&dirp->i_count); + dirp->i_count++; err = dirp->i_op->symlink(dirp, fname, flen, path); fh_unlock(fhp); /* unlock inode */ @@ -693,7 +693,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, /* * Okay, now look up the inode of the new symlink. */ - atomic_inc(&dirp->i_count); /* lookup eats the dirp inode */ + dirp->i_count++; /* lookup eats the dirp inode */ err = dirp->i_op->lookup(dirp, fname, flen, &inode); if (err) return nfserrno(-err); @@ -730,7 +730,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, return nfserr_perm; fh_lock(ffhp); /* lock directory inode */ - atomic_inc(&dirp->i_count); + dirp->i_count++; err = dirp->i_op->link(dest, dirp, fname, len); fh_unlock(ffhp); /* unlock inode */ @@ -770,8 +770,8 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, return nfserr_perm; fh_lock(tfhp); /* lock destination directory */ - atomic_inc(&tdir->i_count); - atomic_inc(&fdir->i_count); + tdir->i_count++; + fdir->i_count++; err = fdir->i_op->rename(fdir, fname, flen, tdir, tname, tlen); fh_unlock(tfhp); /* unlock inode */ @@ -805,12 +805,12 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, if (type == S_IFDIR) { if (!dirp->i_op || !dirp->i_op->rmdir) return nfserr_notdir; - atomic_inc(&dirp->i_count); + dirp->i_count++; err = dirp->i_op->rmdir(dirp, fname, flen); } else { /* other than S_IFDIR */ if (!dirp->i_op || !dirp->i_op->unlink) return nfserr_perm; - atomic_inc(&dirp->i_count); + dirp->i_count++; err = dirp->i_op->unlink(dirp, fname, flen); } @@ -4,7 +4,6 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include <linux/config.h> #include <linux/vfs.h> #include <linux/types.h> #include <linux/utime.h> @@ -21,32 +20,27 @@ #include <linux/file.h> #include <linux/smp.h> #include <linux/smp_lock.h> -#include <linux/omirr.h> #include <asm/uaccess.h> #include <asm/bitops.h> asmlinkage int sys_statfs(const char * path, struct statfs * buf) { - struct inode * inode; + struct dentry * dentry; int error; lock_kernel(); - error = verify_area(VERIFY_WRITE, buf, sizeof(struct statfs)); - if (error) - goto out; - error = namei(NAM_FOLLOW_LINK, path, &inode); - if (error) - goto out; - error = -ENOSYS; - if (!inode->i_sb->s_op->statfs) { - iput(inode); - goto out; + dentry = namei(path); + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + + error = -ENOSYS; + if (inode->i_sb->s_op->statfs) + error = inode->i_sb->s_op->statfs(inode->i_sb, buf, sizeof(struct statfs)); + + dput(dentry); } - inode->i_sb->s_op->statfs(inode->i_sb, buf, sizeof(struct statfs)); - iput(inode); - error = 0; -out: unlock_kernel(); return error; } @@ -54,6 +48,7 @@ out: asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf) { struct inode * inode; + struct dentry * dentry; struct file * file; int error; @@ -63,7 +58,9 @@ asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf) goto out; if (fd >= NR_OPEN || !(file = current->files->fd[fd])) error = -EBADF; - else if (!(inode = file->f_inode)) + else if (!(dentry = file->f_dentry)) + error = -ENOENT; + else if (!(inode = dentry->d_inode)) error = -ENOENT; else if (!inode->i_sb) error = -ENODEV; @@ -90,7 +87,6 @@ int do_truncate(struct inode *inode, unsigned long length) vmtruncate(inode, length); if (inode->i_op && inode->i_op->truncate) inode->i_op->truncate(inode); - inode->i_status |= ST_MODIFIED; } up(&inode->i_sem); return error; @@ -98,33 +94,37 @@ int do_truncate(struct inode *inode, unsigned long length) asmlinkage int sys_truncate(const char * path, unsigned long length) { + struct dentry * dentry; struct inode * inode; int error; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, path, &inode); - if (error) + dentry = namei(path); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + inode = dentry->d_inode; error = -EACCES; if (S_ISDIR(inode->i_mode)) - goto iput_and_out; + goto dput_and_out; error = permission(inode,MAY_WRITE); if (error) - goto iput_and_out; + goto dput_and_out; error = -EROFS; if (IS_RDONLY(inode)) - goto iput_and_out; + goto dput_and_out; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto iput_and_out; + goto dput_and_out; error = get_write_access(inode); if (error) - goto iput_and_out; + goto dput_and_out; error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL, length < inode->i_size ? length : inode->i_size, @@ -135,8 +135,8 @@ asmlinkage int sys_truncate(const char * path, unsigned long length) error = do_truncate(inode, length); } put_write_access(inode); -iput_and_out: - iput(inode); +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -145,13 +145,16 @@ out: asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length) { struct inode * inode; + struct dentry *dentry; struct file * file; int error; lock_kernel(); if (fd >= NR_OPEN || !(file = current->files->fd[fd])) error = -EBADF; - else if (!(inode = file->f_inode)) + else if (!(dentry = file->f_dentry)) + error = -ENOENT; + else if (!(inode = dentry->d_inode)) error = -ENOENT; else if (S_ISDIR(inode->i_mode) || !(file->f_mode & FMODE_WRITE)) error = -EACCES; @@ -184,17 +187,21 @@ asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length) asmlinkage int sys_utime(char * filename, struct utimbuf * times) { int error; + struct dentry * dentry; struct inode * inode; struct iattr newattrs; lock_kernel(); - /* Hmm, should I always follow symlinks or not ? */ - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + inode = dentry->d_inode; + error = -EROFS; if (IS_RDONLY(inode)) - goto iput_and_out; + goto dput_and_out; /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; @@ -203,22 +210,17 @@ asmlinkage int sys_utime(char * filename, struct utimbuf * times) if (!error) error = get_user(newattrs.ia_mtime, ×->modtime); if (error) - goto iput_and_out; + goto dput_and_out; newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; } else { if (current->fsuid != inode->i_uid && (error = permission(inode,MAY_WRITE)) != 0) - goto iput_and_out; + goto dput_and_out; } error = notify_change(inode, &newattrs); -#ifdef CONFIG_OMIRR - if(!error) - omirr_printall(inode, " U %ld %ld %ld ", CURRENT_TIME, - newattrs.ia_atime, newattrs.ia_mtime); -#endif -iput_and_out: - iput(inode); +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -233,38 +235,39 @@ out: asmlinkage int sys_utimes(char * filename, struct timeval * utimes) { int error; + struct dentry * dentry; struct inode * inode; struct iattr newattrs; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + inode = dentry->d_inode; + error = -EROFS; if (IS_RDONLY(inode)) - goto iput_and_out; + goto dput_and_out; + /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (utimes) { struct timeval times[2]; error = -EFAULT; if (copy_from_user(×, utimes, sizeof(times))) - goto iput_and_out; + goto dput_and_out; newattrs.ia_atime = times[0].tv_sec; newattrs.ia_mtime = times[1].tv_sec; newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; } else { if ((error = permission(inode,MAY_WRITE)) != 0) - goto iput_and_out; + goto dput_and_out; } error = notify_change(inode, &newattrs); -#ifdef CONFIG_OMIRR - if(!error) - omirr_printall(inode, " U %ld %ld %ld ", CURRENT_TIME, - newattrs.ia_atime, newattrs.ia_mtime); -#endif -iput_and_out: - iput(inode); +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -276,7 +279,7 @@ out: */ asmlinkage int sys_access(const char * filename, int mode) { - struct inode * inode; + struct dentry * dentry; int old_fsuid, old_fsgid; int res = -EINVAL; @@ -287,11 +290,14 @@ asmlinkage int sys_access(const char * filename, int mode) old_fsgid = current->fsgid; current->fsuid = current->uid; current->fsgid = current->gid; - res = namei(NAM_FOLLOW_LINK, filename, &inode); - if (!res) { - res = permission(inode, mode); - iput(inode); + + dentry = namei(filename); + res = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + res = permission(dentry->d_inode, mode); + dput(dentry); } + current->fsuid = old_fsuid; current->fsgid = old_fsgid; out: @@ -301,24 +307,34 @@ out: asmlinkage int sys_chdir(const char * filename) { - struct inode * inode; - struct inode * tmpi; int error; + struct inode *inode; + struct dentry *dentry, *tmp; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) + + dentry = namei(filename); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + + inode = dentry->d_inode; + error = -ENOTDIR; if (!S_ISDIR(inode->i_mode)) - goto iput_and_out; - if ((error = permission(inode,MAY_EXEC)) != 0) - goto iput_and_out; - - /* exchange inodes */ - tmpi = current->fs->pwd; current->fs->pwd = inode; inode = tmpi; -iput_and_out: - iput(inode); + goto dput_and_out; + + error = permission(inode,MAY_EXEC); + if (error) + goto dput_and_out; + + /* exchange dentries */ + tmp = current->fs->pwd; + current->fs->pwd = dentry; + dentry = tmp; + +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -326,24 +342,38 @@ out: asmlinkage int sys_fchdir(unsigned int fd) { - struct inode * inode; - struct file * file; - int error = -EBADF; + struct file *file; + struct dentry *dentry; + struct inode *inode; + int error; lock_kernel(); + + error = -EBADF; if (fd >= NR_OPEN || !(file = current->files->fd[fd])) goto out; + error = -ENOENT; - if (!(inode = file->f_inode)) + if (!(dentry = file->f_dentry)) goto out; + if (!(inode = dentry->d_inode)) + goto out; + error = -ENOTDIR; if (!S_ISDIR(inode->i_mode)) goto out; - if ((error = permission(inode,MAY_EXEC)) != 0) + + error = permission(inode,MAY_EXEC); + if (error) goto out; - iput(current->fs->pwd); - current->fs->pwd = inode; - atomic_inc(&inode->i_count); + + { + struct dentry *tmp; + + tmp = current->fs->pwd; + current->fs->pwd = dget(dentry); + dput(tmp); + } out: unlock_kernel(); return error; @@ -351,24 +381,39 @@ out: asmlinkage int sys_chroot(const char * filename) { - struct inode * inode; - struct inode * tmpi; int error; + struct inode *inode; + struct dentry *dentry, *tmp; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) + + dentry = namei(filename); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + + inode = dentry->d_inode; + error = -ENOTDIR; if (!S_ISDIR(inode->i_mode)) - goto iput_and_out; + goto dput_and_out; + + error = permission(inode,MAY_EXEC); + if (error) + goto dput_and_out; + error = -EPERM; if (!fsuser()) - goto iput_and_out; - tmpi = current->fs->root; current->fs->root = inode; inode = tmpi; + goto dput_and_out; + + /* exchange dentries */ + tmp = current->fs->root; + current->fs->root = dentry; + dentry = tmp; error = 0; -iput_and_out: - iput(inode); + +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -377,6 +422,7 @@ out: asmlinkage int sys_fchmod(unsigned int fd, mode_t mode) { struct inode * inode; + struct dentry * dentry; struct file * file; struct iattr newattrs; int err = -EBADF; @@ -385,7 +431,9 @@ asmlinkage int sys_fchmod(unsigned int fd, mode_t mode) if (fd >= NR_OPEN || !(file = current->files->fd[fd])) goto out; err = -ENOENT; - if (!(inode = file->f_inode)) + if (!(dentry = file->f_dentry)) + goto out; + if (!(inode = dentry->d_inode)) goto out; err = -EROFS; if (IS_RDONLY(inode)) @@ -397,12 +445,7 @@ asmlinkage int sys_fchmod(unsigned int fd, mode_t mode) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - inode->i_dirt = 1; err = notify_change(inode, &newattrs); -#ifdef CONFIG_OMIRR - if(!err) - omirr_printall(inode, " M %ld %ld ", CURRENT_TIME, newattrs.ia_mode); -#endif out: unlock_kernel(); return err; @@ -410,54 +453,51 @@ out: asmlinkage int sys_chmod(const char * filename, mode_t mode) { + struct dentry * dentry; struct inode * inode; int error; struct iattr newattrs; lock_kernel(); - /* I'm not sure whether to use NAM_FOLLOW_TRAILSLASH instead, - * because permissions on symlinks now can never be changed, - * but on the other hand they are never needed. - */ - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + inode = dentry->d_inode; + error = -EROFS; if (IS_RDONLY(inode)) - goto iput_and_out; + goto dput_and_out; + error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto iput_and_out; + goto dput_and_out; + if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - inode->i_dirt = 1; error = notify_change(inode, &newattrs); -#ifdef CONFIG_OMIRR - if(!error) - omirr_printall(inode, " M %ld %ld ", CURRENT_TIME, newattrs.ia_mode); -#endif -iput_and_out: - iput(inode); + +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; } -asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) +static int chown_common(struct dentry * dentry, uid_t user, gid_t group) { struct inode * inode; - struct file * file; struct iattr newattrs; - int error = -EBADF; + int error; - lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) - goto out; error = -ENOENT; - if (!(inode = file->f_inode)) + if (!(inode = dentry->d_inode)) { + printk("chown_common: NULL inode\n"); goto out; + } error = -EROFS; if (IS_RDONLY(inode)) goto out; @@ -489,7 +529,6 @@ asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) newattrs.ia_mode &= ~S_ISGID; newattrs.ia_valid |= ATTR_MODE; } - inode->i_dirt = 1; if (inode->i_sb && inode->i_sb->dq_op) { inode->i_sb->dq_op->initialize(inode, -1); error = -EDQUOT; @@ -500,80 +539,70 @@ asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) inode->i_sb->dq_op->transfer(inode, &newattrs, 1); } else error = notify_change(inode, &newattrs); -#ifdef CONFIG_OMIRR - if(!error) - omirr_printall(inode, " O %d %d ", CURRENT_TIME, - newattrs.ia_uid, newattrs.ia_gid); -#endif out: - unlock_kernel(); return error; } +asmlinkage int sys_lchown(const char * filename, uid_t user, gid_t group) +{ + struct dentry * dentry; + int error; + + lock_kernel(); + dentry = lnamei(filename); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto out; + + error = chown_common(dentry, user, group); + + dput(dentry); +out: + unlock_kernel(); + return(error); +} + asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) { - struct inode * inode; + struct dentry * dentry; int error; - struct iattr newattrs; lock_kernel(); - error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode); - if (error) + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; - error = -EROFS; - if (IS_RDONLY(inode)) - goto iput_and_out; - error = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto iput_and_out; - if (user == (uid_t) -1) - user = inode->i_uid; - if (group == (gid_t) -1) - group = inode->i_gid; - newattrs.ia_mode = inode->i_mode; - newattrs.ia_uid = user; - newattrs.ia_gid = group; - newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME; - /* - * If the owner has been changed, remove the setuid bit - */ - if (inode->i_mode & S_ISUID) { - newattrs.ia_mode &= ~S_ISUID; - newattrs.ia_valid |= ATTR_MODE; - } - /* - * If the group has been changed, remove the setgid bit - * - * Don't remove the setgid bit if no group execute bit. - * This is a file marked for mandatory locking. - */ - if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) { - newattrs.ia_mode &= ~S_ISGID; - newattrs.ia_valid |= ATTR_MODE; - } - inode->i_dirt = 1; - if (inode->i_sb->dq_op) { - inode->i_sb->dq_op->initialize(inode, -1); - error = -EDQUOT; - if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0)) - goto iput_and_out; - error = notify_change(inode, &newattrs); - if (error) - inode->i_sb->dq_op->transfer(inode, &newattrs, 1); - } else - error = notify_change(inode, &newattrs); -#ifdef CONFIG_OMIRR - if(!error) - omirr_printall(inode, " O %d %d ", CURRENT_TIME, - newattrs.ia_uid, newattrs.ia_gid); -#endif -iput_and_out: - iput(inode); + + error = chown_common(dentry, user, group); + + dput(dentry); out: unlock_kernel(); return(error); } +asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) +{ + struct dentry * dentry; + struct file * file; + int error = -EBADF; + + lock_kernel(); + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + goto out; + error = -ENOENT; + if (!(dentry = file->f_dentry)) + goto out; + + error = chown_common(dentry, user, group); + +out: + unlock_kernel(); + return error; +} + /* * Note that while the flag value (low two bits) for sys_open means: * 00 - read-only @@ -591,6 +620,7 @@ out: static int do_open(const char * filename,int flags,int mode, int fd) { struct inode * inode; + struct dentry * dentry; struct file * f; int flag,error; @@ -603,16 +633,18 @@ static int do_open(const char * filename,int flags,int mode, int fd) flag++; if (flag & O_TRUNC) flag |= 2; - error = open_namei(filename,flag,mode,&inode,NULL); - if (error) + dentry = open_namei(filename,flag,mode); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto cleanup_file; + inode = dentry->d_inode; if (f->f_mode & FMODE_WRITE) { error = get_write_access(inode); if (error) - goto cleanup_inode; + goto cleanup_dentry; } - f->f_inode = inode; + f->f_dentry = dentry; f->f_pos = 0; f->f_reada = 0; f->f_op = NULL; @@ -631,8 +663,8 @@ static int do_open(const char * filename,int flags,int mode, int fd) cleanup_all: if (f->f_mode & FMODE_WRITE) put_write_access(inode); -cleanup_inode: - iput(inode); +cleanup_dentry: + dput(dentry); cleanup_file: put_filp(f); return error; @@ -666,14 +698,15 @@ asmlinkage int sys_open(const char * filename,int flags,int mode) int fd, error; lock_kernel(); - fd = get_unused_fd(); - if (fd < 0) { - error = fd; + error = get_unused_fd(); + if (error < 0) goto out; - } - error = getname(filename, &tmp); - if (!error) { - error = do_open(tmp,flags,mode, fd); + + fd = error; + tmp = getname(filename); + error = PTR_ERR(tmp); + if (!IS_ERR(tmp)) { + error = do_open(tmp,flags,mode,fd); putname(tmp); if (!error) { error = fd; @@ -704,31 +737,35 @@ asmlinkage int sys_creat(const char * pathname, int mode) #endif -int __fput(struct file *filp, struct inode *inode) +int __fput(struct file *filp) { int error = 0; + struct dentry * dentry = filp->f_dentry; + struct inode * inode = dentry->d_inode; if (filp->f_op && filp->f_op->release) error = filp->f_op->release(inode,filp); - filp->f_inode = NULL; + filp->f_dentry = NULL; if (filp->f_mode & FMODE_WRITE) put_write_access(inode); - iput(inode); + dput(dentry); return error; } int close_fp(struct file *filp) { + struct dentry *dentry; struct inode *inode; if (filp->f_count == 0) { printk("VFS: Close: file count is 0\n"); return 0; } - inode = filp->f_inode; + dentry = filp->f_dentry; + inode = dentry->d_inode; if (inode) locks_remove_locks(current, filp); - return fput(filp, inode); + return fput(filp); } asmlinkage int sys_close(unsigned int fd) @@ -75,10 +75,7 @@ static long pipe_read(struct inode * inode, struct file * filp, PIPE_LOCK(*inode)--; wake_up_interruptible(&PIPE_WAIT(*inode)); if (read) { - if (DO_UPDATE_ATIME(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } + UPDATE_ATIME(inode); return read; } if (PIPE_WRITERS(*inode)) @@ -132,7 +129,7 @@ static long pipe_write(struct inode * inode, struct file * filp, free = 1; } inode->i_ctime = inode->i_mtime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); return written; } @@ -168,7 +165,7 @@ static int pipe_ioctl(struct inode *pino, struct file * filp, static unsigned int pipe_poll(struct file * filp, poll_table * wait) { unsigned int mask; - struct inode * inode = filp->f_inode; + struct inode * inode = filp->f_dentry->d_inode; poll_wait(&PIPE_WAIT(*inode), wait); mask = POLLIN | POLLRDNORM; @@ -189,7 +186,7 @@ static unsigned int pipe_poll(struct file * filp, poll_table * wait) static unsigned int fifo_poll(struct file * filp, poll_table * wait) { unsigned int mask; - struct inode * inode = filp->f_inode; + struct inode * inode = filp->f_dentry->d_inode; poll_wait(&PIPE_WAIT(*inode), wait); mask = POLLIN | POLLRDNORM; @@ -221,7 +218,7 @@ static long connect_read(struct inode * inode, struct file * filp, static unsigned int connect_poll(struct file * filp, poll_table * wait) { - struct inode * inode = filp->f_inode; + struct inode * inode = filp->f_dentry->d_inode; poll_wait(&PIPE_WAIT(*inode), wait); if (!PIPE_EMPTY(*inode)) { @@ -233,18 +230,26 @@ static unsigned int connect_poll(struct file * filp, poll_table * wait) return POLLOUT | POLLWRNORM; } -static int pipe_read_release(struct inode * inode, struct file * filp) +static int pipe_release(struct inode * inode) { - PIPE_READERS(*inode)--; + if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) { + free_page((unsigned long) PIPE_BASE(*inode)); + PIPE_BASE(*inode) = NULL; + } wake_up_interruptible(&PIPE_WAIT(*inode)); return 0; } +static int pipe_read_release(struct inode * inode, struct file * filp) +{ + PIPE_READERS(*inode)--; + return pipe_release(inode); +} + static int pipe_write_release(struct inode * inode, struct file * filp) { PIPE_WRITERS(*inode)--; - wake_up_interruptible(&PIPE_WAIT(*inode)); - return 0; + return pipe_release(inode); } static int pipe_rdwr_release(struct inode * inode, struct file * filp) @@ -253,8 +258,7 @@ static int pipe_rdwr_release(struct inode * inode, struct file * filp) PIPE_READERS(*inode)--; if (filp->f_mode & FMODE_WRITE) PIPE_WRITERS(*inode)--; - wake_up_interruptible(&PIPE_WAIT(*inode)); - return 0; + return pipe_release(inode); } static int pipe_read_open(struct inode * inode, struct file * filp) @@ -373,6 +377,42 @@ struct file_operations rdwr_pipe_fops = { NULL }; +static struct inode * get_pipe_inode(void) +{ + extern struct inode_operations pipe_inode_operations; + struct inode *inode = get_empty_inode(); + + if (inode) { + unsigned long page = __get_free_page(GFP_USER); + + if (!page) { + iput(inode); + inode = NULL; + } else { + PIPE_BASE(*inode) = (char *) page; + inode->i_op = &pipe_inode_operations; + PIPE_WAIT(*inode) = NULL; + PIPE_START(*inode) = PIPE_LEN(*inode) = 0; + PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; + PIPE_LOCK(*inode) = 0; + /* + * Mark the inode dirty from the very beginning, + * that way it will never be moved to the dirty + * list because "make_inode_dirty()" will think + * that it already _is_ on the dirty list. + */ + inode->i_state = 1 << I_DIRTY; + inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_blksize = PAGE_SIZE; + } + } + return inode; +} + struct inode_operations pipe_inode_operations = { &rdwr_pipe_fops, NULL, /* create */ @@ -422,12 +462,14 @@ int do_pipe(int *fd) goto close_f12_inode_i; j = error; - f1->f_inode = f2->f_inode = inode; + f1->f_dentry = f2->f_dentry = dget(d_alloc_root(inode, NULL)); + /* read file */ f1->f_pos = f2->f_pos = 0; f1->f_flags = O_RDONLY; f1->f_op = &read_pipe_fops; f1->f_mode = 1; + /* write file */ f2->f_flags = O_WRONLY; f2->f_op = &write_pipe_fops; @@ -441,7 +483,6 @@ int do_pipe(int *fd) close_f12_inode_i: put_unused_fd(i); close_f12_inode: - atomic_dec(&inode->i_count); iput(inode); close_f12: put_filp(f2); diff --git a/fs/proc/Makefile b/fs/proc/Makefile index 6f336245d..68e2a3485 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile... O_TARGET := proc.o -O_OBJS := inode.o root.o base.o generic.o mem.o link.o arbitrary.o fd.o array.o \ +O_OBJS := inode.o root.o base.o generic.o mem.o link.o fd.o array.o \ kmsg.o scsi.o proc_tty.o ifdef CONFIG_OMIRR O_OBJS := $(O_OBJS) omirr.o diff --git a/fs/proc/arbitrary.c b/fs/proc/arbitrary.c deleted file mode 100644 index 1e18e594e..000000000 --- a/fs/proc/arbitrary.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * $Id: arbitrary.c,v 1.2 1997/06/05 01:27:47 davem Exp $ - * - * linux/fs/proc/arbitrary.c - lookup() for arbitrary inodes. - * Copyright (C) 1997, Thomas Schoebel-Theuer, - * <schoebel@informatik.uni-stuttgart.de>. - */ - -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/kdev_t.h> -#include <linux/fs.h> - -/* Format of dev/inode pairs that can be used as file names: - * [<dev_number_in_hex]:<inode_number_in_decimal> - * (the same format that is already in use in /proc/<pid>/exe, - * /proc/<pid>/cwd and /proc/<pid>/root). - */ -/* Note that readdir does not supply such names, so they must be used - * either "blind" or must be queried another way, for example - * as result of a virtual symlink (see linux/proc/link.c). - */ -int proc_arbitrary_lookup(struct inode * dir, const char * name, - int len, struct inode ** result) -{ - int dev, ino; - char * ptr = (char*)name; - kdev_t kdev; - int i; - int error = -EINVAL; - - if(*ptr++ != '[') - goto done; - dev = simple_strtoul(ptr, &ptr, 16); - if(*ptr++ != ']') - goto done; - if(*ptr++ != ':') - goto done; - ino = simple_strtoul(ptr, &ptr, 0); - if((long)ptr - (long)name != len) - goto done; - - error = -ENOENT; - kdev = to_kdev_t(dev); - if(!kdev) - goto done; - for(i = 0; i < NR_SUPER; i++) - if(super_blocks[i].s_dev == kdev) - break; - if(i < NR_SUPER) { - *result = iget(&super_blocks[i], ino); - if(*result) - error = 0; - } -done: - iput(dir); - return error; -} diff --git a/fs/proc/array.c b/fs/proc/array.c index 518ef1b4c..773b96873 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -612,7 +612,7 @@ static inline char * task_mem(struct task_struct *p, char *buffer) for (vma = mm->mmap; vma; vma = vma->vm_next) { unsigned long len = (vma->vm_end - vma->vm_start) >> 10; - if (!vma->vm_inode) { + if (!vma->vm_dentry) { data += len; if (vma->vm_flags & VM_GROWSDOWN) stack += len; @@ -970,12 +970,11 @@ static long read_maps (int pid, struct file * file, *cp++ = flags & VM_MAYSHARE ? 's' : 'p'; *cp++ = 0; - if (map->vm_inode != NULL) { - dev = map->vm_inode->i_dev; - ino = map->vm_inode->i_ino; - } else { - dev = 0; - ino = 0; + dev = 0; + ino = 0; + if (map->vm_dentry != NULL) { + dev = map->vm_dentry->d_inode->i_dev; + ino = map->vm_dentry->d_inode->i_ino; } len = sprintf(line, @@ -1237,6 +1236,7 @@ struct inode_operations proc_array_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -1282,6 +1282,7 @@ struct inode_operations proc_arraylong_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/base.c b/fs/proc/base.c index b983e73f6..7e9a65e08 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -42,6 +42,7 @@ static struct inode_operations proc_base_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 884631db8..1e1fb494f 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -14,7 +14,7 @@ #include <linux/stat.h> static int proc_readfd(struct inode *, struct file *, void *, filldir_t); -static int proc_lookupfd(struct inode *,const char *,int,struct inode **); +static int proc_lookupfd(struct inode *, struct dentry *); static struct file_operations proc_fd_operations = { NULL, /* lseek - default */ @@ -44,6 +44,7 @@ struct inode_operations proc_fd_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -51,39 +52,36 @@ struct inode_operations proc_fd_inode_operations = { NULL /* permission */ }; -static int proc_lookupfd(struct inode * dir, const char * name, int len, - struct inode ** result) +/* + * NOTE! Normally we'd indicate that a file does not + * exist by creating a negative dentry and returning + * a successful return code. However, for this case + * we do not want to create negative dentries, because + * the state of the world can change behind our backs. + * + * Thus just return -ENOENT instead. + */ +static int proc_lookupfd(struct inode * dir, struct dentry * dentry) { unsigned int ino, pid, fd, c; struct task_struct * p; struct super_block * sb; + struct inode *inode; + const char *name; + int len; - *result = NULL; ino = dir->i_ino; pid = ino >> 16; ino &= 0x0000ffff; if (!dir) return -ENOENT; sb = dir->i_sb; - if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) { - iput(dir); + if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) return -ENOENT; - } - if (!len || (name[0] == '.' && (len == 1 || - (name[1] == '.' && len == 2)))) { - if (len < 2) { - *result = dir; - return 0; - } - if (!(*result = proc_get_inode(sb, (pid << 16)+PROC_PID_INO, &proc_pid))) { - iput(dir); - return -ENOENT; - } - iput(dir); - return 0; - } - iput(dir); + fd = 0; + len = dentry->d_name.len; + name = dentry->d_name.name; while (len-- > 0) { c = *name - '0'; name++; @@ -111,13 +109,16 @@ static int proc_lookupfd(struct inode * dir, const char * name, int len, if (fd >= NR_OPEN || !p->files || !p->files->fd[fd] || - !p->files->fd[fd]->f_inode) + !p->files->fd[fd]->f_dentry) return -ENOENT; ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd; - if (!(*result = proc_get_inode(sb, ino, NULL))) + inode = proc_get_inode(sb, ino, NULL); + if (!inode) return -ENOENT; + + d_add(dentry, inode); return 0; } @@ -155,7 +156,7 @@ static int proc_readfd(struct inode * inode, struct file * filp, for (fd -= 2 ; fd < NR_OPEN; fd++, filp->f_pos++) { if (!p->files) break; - if (!p->files->fd[fd] || !p->files->fd[fd]->f_inode) + if (!p->files->fd[fd] || !p->files->fd[fd]->f_dentry) continue; j = NUMBUF; diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 1424dd1ef..358060020 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -23,6 +23,15 @@ static long proc_file_write(struct inode * inode, struct file * file, static long long proc_file_lseek(struct inode * inode, struct file * file, long long offset, int orig); +int proc_match(int len, const char *name,struct proc_dir_entry * de) +{ + if (!de || !de->low_ino) + return 0; + if (de->namelen != len) + return 0; + return !memcmp(name, de->name, len); +} + static struct file_operations proc_file_operations = { proc_file_lseek, /* lseek */ proc_file_read, /* read */ @@ -51,6 +60,7 @@ struct inode_operations proc_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -73,6 +83,7 @@ struct inode_operations proc_net_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 943137bf4..4bc4a4f17 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -25,8 +25,14 @@ static void proc_put_inode(struct inode *inode) && proc_openprom_use) (*proc_openprom_use)(inode, 0); #endif - if (inode->i_nlink) - return; +} + +/* + * Does this ever happen? + */ +static void proc_delete_inode(struct inode *inode) +{ + printk("proc_delete_inode()?\n"); inode->i_size = 0; } @@ -39,9 +45,10 @@ static void proc_put_super(struct super_block *sb) static struct super_operations proc_sops = { proc_read_inode, - NULL, proc_write_inode, proc_put_inode, + proc_delete_inode, + NULL, proc_put_super, NULL, proc_statfs, @@ -131,16 +138,17 @@ struct super_block *proc_read_super(struct super_block *s,void *data, s->s_magic = PROC_SUPER_MAGIC; s->s_op = &proc_sops; unlock_super(s); - if (!(s->s_mounted = proc_get_inode(s, PROC_ROOT_INO, &proc_root))) { + s->s_root = d_alloc_root(proc_get_inode(s, PROC_ROOT_INO, &proc_root), NULL); + if (!s->s_root) { s->s_dev = 0; printk("get root inode failed\n"); return NULL; } - parse_options(data, &s->s_mounted->i_uid, &s->s_mounted->i_gid); + parse_options(data, &s->s_root->d_inode->i_uid, &s->s_root->d_inode->i_gid); return s; } -void proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +int proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; @@ -152,7 +160,7 @@ void proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) tmp.f_files = 0; tmp.f_ffree = 0; tmp.f_namelen = NAME_MAX; - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } void proc_read_inode(struct inode * inode) @@ -200,5 +208,4 @@ void proc_read_inode(struct inode * inode) void proc_write_inode(struct inode * inode) { - inode->i_dirt=0; } diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index 6ef386ffa..1cc6a9c83 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -70,6 +70,7 @@ struct inode_operations proc_kmsg_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/link.c b/fs/proc/link.c index 695ed9bba..c25fd702b 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -14,9 +14,9 @@ #include <linux/mm.h> #include <linux/proc_fs.h> #include <linux/stat.h> -#include <linux/dalloc.h> static int proc_readlink(struct inode *, char *, int); +static struct dentry * proc_follow_link(struct inode *, struct dentry *); /* * PLAN9_SEMANTICS won't work any more: it used an ugly hack that broke @@ -52,6 +52,7 @@ struct inode_operations proc_link_inode_operations = { NULL, /* mknod */ NULL, /* rename */ proc_readlink, /* readlink */ + proc_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -59,59 +60,52 @@ struct inode_operations proc_link_inode_operations = { NULL /* permission */ }; -/* [Feb-1997 T. Schoebel-Theuer] This is no longer called from the - * VFS, but only from proc_readlink(). All the functionality - * should the moved there (without using temporary inodes any more) - * and then it could be eliminated. - */ -static int proc_follow_link(struct inode * dir, struct inode * inode, - int flag, int mode, struct inode ** res_inode) +static struct dentry * proc_follow_link(struct inode *inode, struct dentry *base) { - unsigned int pid, ino; - struct task_struct * p; - struct inode * new_inode; + struct task_struct *p; + struct dentry * result; + int ino, pid; int error; - *res_inode = NULL; - if (dir) - iput(dir); - if (!inode) - return -ENOENT; - if ((error = permission(inode, MAY_EXEC)) != 0){ - iput(inode); - return error; - } + /* We don't need a base pointer in the /proc filesystem */ + dput(base); + + error = permission(inode, MAY_EXEC); + result = ERR_PTR(error); + if (error) + return result; + ino = inode->i_ino; pid = ino >> 16; ino &= 0x0000ffff; p = find_task_by_pid(pid); - if (!p) { - iput(inode); - return -ENOENT; - } - new_inode = NULL; + result = ERR_PTR(-ENOENT); + if (!p) + return result; + switch (ino) { case PROC_PID_CWD: - if (!p->fs) + if (!p->fs || !p->fs->pwd) break; - new_inode = p->fs->pwd; + result = dget(p->fs->pwd); break; + case PROC_PID_ROOT: - if (!p->fs) + if (!p->fs || !p->fs->root) break; - new_inode = p->fs->root; + result = dget(p->fs->root); break; + case PROC_PID_EXE: { struct vm_area_struct * vma; if (!p->mm) break; vma = p->mm->mmap; while (vma) { - if (vma->vm_flags & VM_EXECUTABLE) { - new_inode = vma->vm_inode; - break; - } + if (vma->vm_flags & VM_EXECUTABLE) + return dget(vma->vm_dentry); + vma = vma->vm_next; } break; @@ -122,45 +116,38 @@ static int proc_follow_link(struct inode * dir, struct inode * inode, if (!p->files) break; ino &= 0xff; - if (ino < NR_OPEN && p->files->fd[ino]) { - new_inode = p->files->fd[ino]->f_inode; - } + if (ino >= NR_OPEN) + break; + if (!p->files->fd[ino]) + break; + if (!p->files->fd[ino]->f_dentry) + break; + result = dget(p->files->fd[ino]->f_dentry); break; } } - iput(inode); - if (!new_inode) - return -ENOENT; - *res_inode = new_inode; - atomic_inc(&new_inode->i_count); - return 0; + return result; } static int proc_readlink(struct inode * inode, char * buffer, int buflen) { - int error = proc_follow_link(NULL, inode, 0, 0, &inode); + int error; + struct dentry * dentry = proc_follow_link(inode, NULL); - if (error) - return error; - if (!inode) - return -EIO; - - /* This will return *one* of the alias names (which is not quite - * correct). I have to rethink the problem, so this is only a - * quick hack... - */ - if(inode->i_dentry) { - char * tmp = (char*)__get_free_page(GFP_KERNEL); - int len = d_path(inode->i_dentry, current->fs->root, tmp); - int min = buflen<PAGE_SIZE ? buflen : PAGE_SIZE; - if(len <= min) - min = len+1; - copy_to_user(buffer, tmp, min); - free_page((unsigned long)tmp); - error = len; - } else { - error= -ENOENT; + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + error = -ENOENT; + if (dentry) { + char * tmp = (char*)__get_free_page(GFP_KERNEL); + int len = d_path(dentry, current->fs->root, tmp); + int min = buflen<PAGE_SIZE ? buflen : PAGE_SIZE; + if(len <= min) + min = len+1; + dput(dentry); + copy_to_user(buffer, tmp, min); + free_page((unsigned long)tmp); + error = len; + } } - iput(inode); return error; } diff --git a/fs/proc/mem.c b/fs/proc/mem.c index a64ead624..97acb5ee8 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -328,6 +328,7 @@ struct inode_operations proc_mem_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/net.c b/fs/proc/net.c index 3bc5c339c..257487569 100644 --- a/fs/proc/net.c +++ b/fs/proc/net.c @@ -111,6 +111,7 @@ struct inode_operations proc_net_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/omirr.c b/fs/proc/omirr.c index 0e6377fb2..041e493d2 100644 --- a/fs/proc/omirr.c +++ b/fs/proc/omirr.c @@ -7,7 +7,6 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/fs.h> -#include <linux/dalloc.h> #include <linux/omirr.h> #include <asm/uaccess.h> @@ -288,6 +287,7 @@ struct inode_operations proc_omirr_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/openpromfs.c b/fs/proc/openpromfs.c index 7d741cfaf..346e72ed1 100644 --- a/fs/proc/openpromfs.c +++ b/fs/proc/openpromfs.c @@ -1,7 +1,7 @@ -/* $Id: openpromfs.c,v 1.15 1997/06/05 01:28:11 davem Exp $ +/* $Id: openpromfs.c,v 1.18 1997/07/17 02:24:01 davem Exp $ * openpromfs.c: /proc/openprom handling routines * - * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/module.h> @@ -68,7 +68,7 @@ static long nodenum_read(struct inode *inode, struct file *file, if (count < 0 || !inode->u.generic_ip) return -EINVAL; - sprintf (buffer, "%8.8x\n", (u32)(inode->u.generic_ip)); + sprintf (buffer, "%8.8x\n", (u32)(long)(inode->u.generic_ip)); if (file->f_pos >= 9) return 0; if (count > 9 - file->f_pos) @@ -86,27 +86,28 @@ static long property_read(struct inode *inode, struct file *filp, char *p; u32 *q; openprom_property *op; + char buffer[64]; if (filp->f_pos >= 0xffffff) return -EINVAL; if (!filp->private_data) { - node = nodes[(u16)((uint)inode->u.generic_ip)].node; - i = ((u32)inode->u.generic_ip) >> 16; - if ((u16)((uint)inode->u.generic_ip) == aliases) { + node = nodes[(u16)((long)inode->u.generic_ip)].node; + i = ((u32)(long)inode->u.generic_ip) >> 16; + if ((u16)((long)inode->u.generic_ip) == aliases) { if (i >= aliases_nodes) p = 0; else p = alias_names [i]; } else - for (p = prom_firstprop (node); + for (p = prom_firstprop (node, buffer); i && p && *p; - p = prom_nextprop (node, p), i--) + p = prom_nextprop (node, p, buffer), i--) /* nothing */ ; if (!p || !*p) return -EIO; i = prom_getproplen (node, p); if (i < 0) { - if ((u16)((uint)inode->u.generic_ip) == aliases) + if ((u16)((long)inode->u.generic_ip) == aliases) i = 0; else return -EIO; @@ -155,7 +156,7 @@ static long property_read(struct inode *inode, struct file *filp, if (count > i - k) count = i - k; if (op->flag & OPP_STRING) { if (!k) { - *buf = '\''; + __put_user('\'', buf); k++; count--; } @@ -170,9 +171,9 @@ static long property_read(struct inode *inode, struct file *filp, k += j; } if (count) - buf [k++ - filp->f_pos] = '\''; + __put_user('\'', &buf [k++ - filp->f_pos]); if (count > 1) - buf [k++ - filp->f_pos] = '\n'; + __put_user('\n', &buf [k++ - filp->f_pos]); } else if (op->flag & OPP_BINARY) { char buffer[10]; u32 *first, *last; @@ -186,26 +187,26 @@ static long property_read(struct inode *inode, struct file *filp, if (first == last) { sprintf (buffer, "%08x.", *first); - memcpy (buf, buffer + first_off, last_cnt - first_off); + copy_to_user (buf, buffer + first_off, last_cnt - first_off); buf += last_cnt - first_off; } else { for (q = first; q <= last; q++) { sprintf (buffer, "%08x.", *q); if (q == first) { - memcpy (buf, buffer + first_off, - 9 - first_off); + copy_to_user (buf, buffer + first_off, + 9 - first_off); buf += 9 - first_off; } else if (q == last) { - memcpy (buf, buffer, last_cnt); + copy_to_user (buf, buffer, last_cnt); buf += last_cnt; } else { - memcpy (buf, buffer, 9); + copy_to_user (buf, buffer, 9); buf += 9; } } } if (last == (u32 *)(op->value + op->len - 4) && last_cnt == 9) - *(buf - 1) = '\n'; + __put_user('\n', (buf - 1)); k += count; } count = k - filp->f_pos; @@ -242,8 +243,10 @@ static long property_write(struct inode *inode, struct file *filp, for (i = 0; i < count; i++, j++) { if (j == 9) j = 0; if (!j) { - if (buf [i] != '.') { - if (buf [i] != '\n') { + char ctmp; + __get_user(ctmp, &buf[i]); + if (ctmp != '.') { + if (ctmp != '\n') { if (op->flag & OPP_BINARY) return -EINVAL; else @@ -255,10 +258,12 @@ static long property_write(struct inode *inode, struct file *filp, } } } else { - if (buf [i] < '0' || - (buf [i] > '9' && buf [i] < 'A') || - (buf [i] > 'F' && buf [i] < 'a') || - buf [i] > 'f') { + char ctmp; + __get_user(ctmp, &buf[i]); + if (ctmp < '0' || + (ctmp > '9' && ctmp < 'A') || + (ctmp > 'F' && ctmp < 'a') || + ctmp > 'f') { if (op->flag & OPP_BINARY) return -EINVAL; else @@ -292,8 +297,8 @@ static long property_write(struct inode *inode, struct file *filp, last_cnt = (k + count) % 9; if (first + 1 == last) { memset (tmp, '0', 8); - memcpy (tmp + first_off, buf, (count + first_off > 8) ? - 8 - first_off : count); + copy_from_user (tmp + first_off, buf, + (count + first_off > 8) ? 8 - first_off : count); mask = 0xffffffff; mask2 = 0xffffffff; for (j = 0; j < first_off; j++) @@ -312,8 +317,8 @@ static long property_write(struct inode *inode, struct file *filp, if (q == first) { if (first_off < 8) { memset (tmp, '0', 8); - memcpy (tmp + first_off, buf, - 8 - first_off); + copy_from_user (tmp + first_off, buf, + 8 - first_off); mask = 0xffffffff; for (j = 0; j < first_off; j++) mask >>= 1; @@ -324,7 +329,7 @@ static long property_write(struct inode *inode, struct file *filp, } else if ((q == last - 1) && last_cnt && (last_cnt < 8)) { memset (tmp, '0', 8); - memcpy (tmp, buf, last_cnt); + copy_from_user (tmp, buf, last_cnt); mask = 0xffffffff; for (j = 0; j < 8 - last_cnt; j++) mask <<= 1; @@ -332,7 +337,10 @@ static long property_write(struct inode *inode, struct file *filp, *q |= simple_strtoul (tmp, 0, 16); buf += last_cnt; } else { - *q = simple_strtoul (buf, 0, 16); + char tchars[17]; /* XXX yuck... */ + + copy_from_user(tchars, buf, 16); + *q = simple_strtoul (tchars, 0, 16); buf += 9; } } @@ -347,12 +355,15 @@ static long property_write(struct inode *inode, struct file *filp, write_try_string: if (!(op->flag & OPP_BINARY)) { if (!(op->flag & (OPP_QUOTED | OPP_NOTQUOTED))) { + char ctmp; + /* No way, if somebody starts writing from the middle, * we don't know whether he uses quotes around or not */ if (k > 0) return -EINVAL; - if (*buf == '\'') { + __get_user(ctmp, buf); + if (ctmp == '\'') { op->flag |= OPP_QUOTED; buf++; count--; @@ -383,7 +394,7 @@ write_try_string: kfree (b); } p = op->value + filp->f_pos - ((op->flag & OPP_QUOTED) ? 1 : 0); - memcpy (p, buf, count); + copy_from_user (p, buf, count); op->flag |= OPP_DIRTY; for (i = 0; i < count; i++, p++) if (*p == '\n') { @@ -414,8 +425,8 @@ int property_release (struct inode *inode, struct file *filp) if (!op) return 0; - node = nodes[(u16)((uint)inode->u.generic_ip)].node; - if ((u16)((uint)inode->u.generic_ip) == aliases) { + node = nodes[(u16)((long)inode->u.generic_ip)].node; + if ((u16)((long)inode->u.generic_ip) == aliases) { if ((op->flag & OPP_DIRTY) && (op->flag & OPP_STRING)) { char *p = op->name; int i = (op->value - op->name) - strlen (op->name) - 1; @@ -484,6 +495,7 @@ static struct inode_operations openpromfs_prop_inode_ops = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -516,6 +528,7 @@ static struct inode_operations openpromfs_nodenum_inode_ops = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -548,6 +561,7 @@ static struct inode_operations openprom_alias_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -604,6 +618,7 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len, int i; struct inode *inode; struct openpromfs_dev *d = NULL; + char buffer2[64]; *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { @@ -659,9 +674,9 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len, if (!ino) { int j = NODEP2INO(NODE(dir->i_ino).first_prop); if (dirnode != aliases) { - for (p = prom_firstprop (n); + for (p = prom_firstprop (n, buffer2); p && *p; - p = prom_nextprop (n, p)) { + p = prom_nextprop (n, p, buffer2)) { j++; if ((len == strlen (p)) && !strncmp (p, name, len)) { @@ -720,7 +735,7 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len, inode->i_mode = S_IFREG | S_IRUGO; inode->i_op = &openpromfs_nodenum_inode_ops; inode->i_nlink = 1; - inode->u.generic_ip = (void *)(n); + inode->u.generic_ip = (void *)(long)(n); break; case OPFSL_PROPERTY: if ((dirnode == options) && (len == 17) @@ -737,7 +752,7 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len, inode->i_nlink = 1; if (inode->i_size < 0) inode->i_size = 0; - inode->u.generic_ip = (void *)(((u16)dirnode) | + inode->u.generic_ip = (void *)(long)(((u16)dirnode) | (((u16)(ino - NODEP2INO(NODE(dir->i_ino).first_prop) - 1)) << 16)); break; case OPFSL_DEVICE: @@ -761,6 +776,7 @@ static int openpromfs_readdir(struct inode * inode, struct file * filp, u16 node; char *p; struct openpromfs_dev *d; + char buffer2[64]; if (!inode || !S_ISDIR (inode->i_mode)) return -ENOTDIR; ino = inode->i_ino; @@ -813,9 +829,9 @@ static int openpromfs_readdir(struct inode * inode, struct file * filp, } } } else { - for (p = prom_firstprop (n); + for (p = prom_firstprop (n, buffer2); p && *p; - p = prom_nextprop (n, p)) { + p = prom_nextprop (n, p, buffer2)) { j++; if (i) i--; else { @@ -877,7 +893,7 @@ static int openpromfs_create (struct inode *dir, const char *name, int len, inode->i_op = &openpromfs_prop_inode_ops; inode->i_nlink = 1; if (inode->i_size < 0) inode->i_size = 0; - inode->u.generic_ip = (void *)(((u16)aliases) | + inode->u.generic_ip = (void *)(long)(((u16)aliases) | (((u16)(aliases_nodes - 1)) << 16)); *result = inode; return 0; @@ -940,6 +956,7 @@ static u16 get_nodes (u16 parent, u32 node) { char *p; u16 n = last_node++, i; + char buffer[64]; if (check_space (n) < 0) return 0xffff; @@ -961,15 +978,15 @@ static u16 get_nodes (u16 parent, u32 node) } } if (n != aliases) - for (p = prom_firstprop (node); + for (p = prom_firstprop (node, buffer); p && p != (char *)-1 && *p; - p = prom_nextprop (node, p)) + p = prom_nextprop (node, p, buffer)) first_prop++; else { char *q; - for (p = prom_firstprop (node); + for (p = prom_firstprop (node, buffer); p && p != (char *)-1 && *p; - p = prom_nextprop (node, p)) { + p = prom_nextprop (node, p, buffer)) { if (aliases_nodes == ALIASES_NNODES) break; for (i = 0; i < aliases_nodes; i++) @@ -1012,7 +1029,7 @@ void openpromfs_use (struct inode *inode, int inc) static int usec = 0; if (inc) { - if (atomic_read(&inode->i_count) == 1) + if (inode->i_count == 1) usec++; else if (root_fresh && inode->i_ino == PROC_OPENPROM_FIRST) { root_fresh = 0; @@ -1025,10 +1042,10 @@ void openpromfs_use (struct inode *inode, int inc) usec--; } printk ("openpromfs_use: %d %d %d %d\n", - inode->i_ino, inc, usec, atomic_read(&inode->i_count)); + inode->i_ino, inc, usec, inode->i_count); #else if (inc) { - if (atomic_read(&inode->i_count) == 1) + if (inode->i_count == 1) MOD_INC_USE_COUNT; else if (root_fresh && inode->i_ino == PROC_OPENPROM_FIRST) { root_fresh = 0; @@ -1059,8 +1076,10 @@ EXPORT_NO_SYMBOLS; int init_module (void) #endif { +#ifndef __sparc_v9__ if (!romvec->pv_romvers) return RET(ENODEV); +#endif nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0, 0); if (!nodes) { printk (KERN_WARNING "/proc/openprom: can't get free page\n"); diff --git a/fs/proc/procfs_syms.c b/fs/proc/procfs_syms.c index 71c29dd75..d3077ea79 100644 --- a/fs/proc/procfs_syms.c +++ b/fs/proc/procfs_syms.c @@ -38,7 +38,7 @@ EXPORT_SYMBOL(proc_openprom_deregister); static struct file_system_type proc_fs_type = { "proc", - FS_NO_DCACHE, + 0 /* FS_NO_DCACHE doesn't work correctly */, proc_read_super, NULL }; diff --git a/fs/proc/root.c b/fs/proc/root.c index f42557d2c..2b456ca57 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -24,7 +24,7 @@ #define FIRST_PROCESS_ENTRY 256 static int proc_root_readdir(struct inode *, struct file *, void *, filldir_t); -static int proc_root_lookup(struct inode *,const char *,int,struct inode **); +static int proc_root_lookup(struct inode *,struct dentry *); static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0}; @@ -64,6 +64,7 @@ struct inode_operations proc_dir_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -104,6 +105,7 @@ static struct inode_operations proc_root_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -149,14 +151,14 @@ struct proc_dir_entry proc_sys_root = { #if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) static int (*proc_openprom_defreaddir_ptr)(struct inode *, struct file *, void *, filldir_t); -static int (*proc_openprom_deflookup_ptr)(struct inode *, const char *, int, struct inode **); +static int (*proc_openprom_deflookup_ptr)(struct inode *, struct qstr *, struct inode **); void (*proc_openprom_use)(struct inode *, int) = 0; static struct openpromfs_dev *proc_openprom_devices = NULL; static ino_t proc_openpromdev_ino = PROC_OPENPROMD_FIRST; struct inode_operations * proc_openprom_register(int (*readdir)(struct inode *, struct file *, void *, filldir_t), - int (*lookup)(struct inode *, const char *, int, struct inode **), + int (*lookup)(struct inode *, struct qstr *, struct inode **), void (*use)(struct inode *, int), struct openpromfs_dev ***devices) { @@ -218,15 +220,13 @@ proc_openprom_defreaddir(struct inode * inode, struct file * filp, } static int -proc_openprom_deflookup(struct inode * dir,const char * name, int len, - struct inode ** result) +proc_openprom_deflookup(struct inode * dir, struct qstr *str, struct inode ** result) { request_module("openpromfs"); if (proc_openprom_inode_operations.lookup != proc_openprom_deflookup) return proc_openprom_inode_operations.lookup - (dir, name, len, result); - iput(dir); + (dir, str, result); return -ENOENT; } #endif @@ -264,6 +264,7 @@ struct inode_operations proc_openprom_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -350,7 +351,6 @@ static int proc_self_readlink(struct inode * inode, char * buffer, int buflen) int len; char tmp[30]; - iput(inode); len = sprintf(tmp, "%d", current->pid); if (buflen < len) len = buflen; @@ -358,6 +358,15 @@ static int proc_self_readlink(struct inode * inode, char * buffer, int buflen) return len; } +static struct dentry * proc_self_follow_link(struct inode *inode, struct dentry *base) +{ + int len; + char tmp[30]; + + len = sprintf(tmp, "%d", current->pid); + return lookup_dentry(tmp, base, 1); +} + static struct inode_operations proc_self_inode_operations = { NULL, /* no file-ops */ NULL, /* create */ @@ -370,6 +379,7 @@ static struct inode_operations proc_self_inode_operations = { NULL, /* mknod */ NULL, /* rename */ proc_self_readlink, /* readlink */ + proc_self_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -611,93 +621,49 @@ void proc_root_init(void) proc_tty_init(); } - -int proc_match(int len,const char * name,struct proc_dir_entry * de) -{ - if (!de || !de->low_ino) - return 0; - /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ - if (!len && (de->name[0]=='.') && (de->name[1]=='\0')) - return 1; - if (de->namelen != len) - return 0; - return !memcmp(name, de->name, len); -} - -int proc_lookup(struct inode * dir,const char * name, int len, - struct inode ** result) +/* + * Don't create negative dentries here, return -ENOENT by hand + * instead. + */ +int proc_lookup(struct inode * dir, struct dentry *dentry) { + struct inode *inode; struct proc_dir_entry * de; - int ino; - *result = NULL; - if (!dir || !S_ISDIR(dir->i_mode)) { - iput(dir); + if (!dir || !S_ISDIR(dir->i_mode)) return -ENOTDIR; - } de = (struct proc_dir_entry *) dir->u.generic_ip; - if (!de) { - iput(dir); - return -EINVAL; - } - - /* Either remove this as soon as possible due to security problems, - * or uncomment the root-only usage. - */ - - /* Allow generic inode lookups everywhere. - * No other name in /proc must begin with a '['. - */ - if(/*!current->uid &&*/ name[0] == '[') - return proc_arbitrary_lookup(dir,name,len,result); - - /* Special case "." and "..": they aren't on the directory list */ - *result = dir; - if (!len) - return 0; - if (name[0] == '.') { - if (len == 1) - return 0; - if (name[1] == '.' && len == 2) { - struct inode * inode; - inode = proc_get_inode(dir->i_sb, de->parent->low_ino, de->parent); - iput(dir); - if (!inode) - return -EINVAL; - *result = inode; - return 0; + inode = NULL; + if (de) { + for (de = de->subdir; de ; de = de->next) { + if (!de || !de->low_ino) + continue; + if (de->namelen != dentry->d_name.len) + continue; + if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { + int ino = de->low_ino | (dir->i_ino & ~(0xffff)); + inode = proc_get_inode(dir->i_sb, ino, de); + if (!inode) + return -EINVAL; + break; + } } } - - *result = NULL; - for (de = de->subdir; de ; de = de->next) { - if (proc_match(len, name, de)) - break; - } - if (!de) { - iput(dir); + if (!inode) return -ENOENT; - } - ino = de->low_ino | (dir->i_ino & ~(0xffff)); - - if (!(*result = proc_get_inode(dir->i_sb, ino, de))) { - iput(dir); - return -EINVAL; - } - iput(dir); + d_add(dentry, inode); return 0; } -static int proc_root_lookup(struct inode * dir,const char * name, int len, - struct inode ** result) +static int proc_root_lookup(struct inode * dir, struct dentry * dentry) { unsigned int pid, c; - int ino, retval; struct task_struct *p; - - atomic_inc(&dir->i_count); + const char *name; + struct inode *inode; + int len; if (dir->i_ino == PROC_ROOT_INO) { /* check for safety... */ dir->i_nlink = proc_root.nlink; @@ -710,13 +676,12 @@ static int proc_root_lookup(struct inode * dir,const char * name, int len, read_unlock(&tasklist_lock); } - retval = proc_lookup(dir, name, len, result); - if (retval != -ENOENT) { - iput(dir); - return retval; - } + if (!proc_lookup(dir, dentry)) + return 0; pid = 0; + name = dentry->d_name.name; + len = dentry->d_name.len; while (len-- > 0) { c = *name - '0'; name++; @@ -732,16 +697,14 @@ static int proc_root_lookup(struct inode * dir,const char * name, int len, } } p = find_task_by_pid(pid); - if (!pid || !p) { - iput(dir); - return -ENOENT; - } - ino = (pid << 16) + PROC_PID_INO; - if (!(*result = proc_get_inode(dir->i_sb, ino, &proc_pid))) { - iput(dir); - return -EINVAL; + inode = NULL; + if (pid && p) { + unsigned long ino = (pid << 16) + PROC_PID_INO; + inode = proc_get_inode(dir->i_sb, ino, &proc_pid); + if (!inode) + return -EINVAL; } - iput(dir); + d_add(dentry, inode); return 0; } diff --git a/fs/proc/scsi.c b/fs/proc/scsi.c index fd629a75c..b1e77398c 100644 --- a/fs/proc/scsi.c +++ b/fs/proc/scsi.c @@ -69,6 +69,7 @@ struct inode_operations proc_scsi_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/read_write.c b/fs/read_write.c index 81b19ac30..6c422b7f4 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -60,13 +60,15 @@ asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin) { long retval; struct file * file; + struct dentry * dentry; struct inode * inode; lock_kernel(); retval = -EBADF; if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || - !(inode = file->f_inode)) + !(dentry = file->f_dentry) || + !(inode = dentry->d_inode)) goto bad; retval = -EINVAL; if (origin > 2) @@ -83,6 +85,7 @@ asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high, { long retval; struct file * file; + struct dentry * dentry; struct inode * inode; long long offset; @@ -90,7 +93,8 @@ asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high, retval = -EBADF; if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || - !(inode = file->f_inode)) + !(dentry = file->f_dentry) || + !(inode = dentry->d_inode)) goto bad; retval = -EINVAL; if (origin > 2) @@ -115,6 +119,7 @@ asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count) { int error; struct file * file; + struct dentry * dentry; struct inode * inode; long (*read)(struct inode *, struct file *, char *, unsigned long); @@ -123,7 +128,10 @@ asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count) file = fget(fd); if (!file) goto bad_file; - inode = file->f_inode; + dentry = file->f_dentry; + if (!dentry) + goto bad_file; + inode = dentry->d_inode; if (!inode) goto out; error = -EBADF; @@ -137,7 +145,7 @@ asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count) goto out; error = read(inode,file,buf,count); out: - fput(file, inode); + fput(file); bad_file: unlock_kernel(); return error; @@ -147,6 +155,7 @@ asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count { int error; struct file * file; + struct dentry * dentry; struct inode * inode; long (*write)(struct inode *, struct file *, const char *, unsigned long); @@ -155,7 +164,10 @@ asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count file = fget(fd); if (!file) goto bad_file; - inode = file->f_inode; + dentry = file->f_dentry; + if (!dentry) + goto out; + inode = dentry->d_inode; if (!inode) goto out; if (!(file->f_mode & 2)) @@ -168,10 +180,9 @@ asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count goto out; down(&inode->i_sem); error = write(inode,file,buf,count); - inode->i_status |= ST_MODIFIED; up(&inode->i_sem); out: - fput(file, inode); + fput(file); bad_file: unlock_kernel(); return error; @@ -264,8 +275,6 @@ static long do_readv_writev(int type, struct inode * inode, struct file * file, if (nr != len) break; } - if(fn == (IO_fn_t) file->f_op->write) - inode->i_status |= ST_MODIFIED; if (iov != iovstack) kfree(iov); return retval; @@ -274,14 +283,30 @@ static long do_readv_writev(int type, struct inode * inode, struct file * file, asmlinkage long sys_readv(unsigned long fd, const struct iovec * vector, unsigned long count) { struct file * file; + struct dentry * dentry; struct inode * inode; - long err = -EBADF; + long err; lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode=file->f_inode)) + err = -EBADF; + if (fd >= NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if (!file) goto out; + if (!(file->f_mode & 1)) goto out; + + dentry = file->f_dentry; + if (!dentry) + goto out; + + inode = dentry->d_inode; + if (!inode) + goto out; + err = do_readv_writev(VERIFY_WRITE, inode, file, vector, count); out: unlock_kernel(); @@ -290,15 +315,32 @@ out: asmlinkage long sys_writev(unsigned long fd, const struct iovec * vector, unsigned long count) { - int error = -EBADF; + long error; struct file * file; + struct dentry * dentry; struct inode * inode; lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode=file->f_inode)) + error = -EBADF; + + if (fd >= NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if (!file) goto out; + if (!(file->f_mode & 2)) goto out; + + dentry = file->f_dentry; + if (!dentry) + goto out; + + inode = dentry->d_inode; + if (!inode) + goto out; + down(&inode->i_sem); error = do_readv_writev(VERIFY_READ, inode, file, vector, count); up(&inode->i_sem); diff --git a/fs/readdir.c b/fs/readdir.c index a86398ac3..2a2b0a707 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -1,35 +1,20 @@ /* - * fs/readdir.c + * linux/fs/readdir.c * * Copyright (C) 1995 Linus Torvalds */ -#include <linux/config.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/stat.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> -#ifdef CONFIG_TRANS_NAMES -#include <linux/nametrans.h> -#endif -#include <linux/dalloc.h> #include <linux/smp.h> #include <linux/smp_lock.h> #include <asm/uaccess.h> -/* [T.Schoebel-Theuer] I am assuming that directories never get too large. - * The problem is that getdents() delivers d_offset's that can be used - * for lseek() by the user, so I must encode the status information for - * name translation and dcache baskets in the offset. - * Note that the linux man page getdents(2) does not mention that - * the d_offset is fs-specific and can be used for lseek(). - */ -#define BASKET_BIT (1<<30) /* 31 is already used by affs */ -#define TRANS_BIT (1<<29) - /* * Traditional linux readdir() handling.. * @@ -50,9 +35,6 @@ struct old_linux_dirent { struct readdir_callback { struct old_linux_dirent * dirent; - struct file * file; - int translate; - off_t oldoffset; int count; }; @@ -65,60 +47,49 @@ static int fillonedir(void * __buf, const char * name, int namlen, off_t offset, return -EINVAL; buf->count++; dirent = buf->dirent; - copy_to_user(dirent->d_name, name, namlen); - put_user(0, dirent->d_name + namlen); -#ifdef CONFIG_TRANS_NAMES - if(!buf->translate) { - char * cut; -#ifdef CONFIG_TRANS_RESTRICT - struct inode * inode = buf->file->f_inode; - cut = testname(inode && inode->i_gid != CONFIG_TRANS_GID, dirent->d_name); -#else - cut = testname(1, dirent->d_name); -#endif - if(cut) { - put_user(0, cut); - buf->translate = 1; - } - } -#endif put_user(ino, &dirent->d_ino); put_user(offset, &dirent->d_offset); put_user(namlen, &dirent->d_namlen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); return 0; } asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count) { - int error = -EBADF; + int error; struct file * file; + struct dentry * dentry; + struct inode * inode; struct readdir_callback buf; - off_t oldpos; lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + error = -EBADF; + if (fd >= NR_OPEN) goto out; - error = -ENOTDIR; - if (!file->f_op || !file->f_op->readdir) + + file = current->files->fd[fd]; + if (!file) goto out; - error = verify_area(VERIFY_WRITE, dirent, sizeof(struct old_linux_dirent)); - if (error) + + dentry = file->f_dentry; + if (!dentry) goto out; - oldpos = file->f_pos; - buf.file = file; - buf.dirent = dirent; + + inode = dentry->d_inode; + if (!inode) + goto out; + buf.count = 0; - buf.translate = 0; - if(file->f_pos & TRANS_BIT) { - file->f_pos &= ~TRANS_BIT; - buf.translate = 1; - } - error = file->f_op->readdir(file->f_inode, file, &buf, fillonedir); + buf.dirent = dirent; + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = file->f_op->readdir(inode, file, &buf, fillonedir); if (error < 0) goto out; - if(buf.translate) { - file->f_pos = oldpos | TRANS_BIT; - } error = buf.count; out: unlock_kernel(); @@ -139,11 +110,8 @@ struct linux_dirent { struct getdents_callback { struct linux_dirent * current_dir; struct linux_dirent * previous; - struct file * file; int count; - int error; - int restricted; - int do_preload; + int error; }; static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino) @@ -152,51 +120,18 @@ static int filldir(void * __buf, const char * name, int namlen, off_t offset, in struct getdents_callback * buf = (struct getdents_callback *) __buf; int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); - /* Do not touch buf->error any more if everything is ok! */ + buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) - return (buf->error = -EINVAL); -#ifdef CONFIG_DCACHE_PRELOAD - if(buf->do_preload && (name[0] != '.' || namlen > 2)) { - struct qstr qname = { name, namlen }; - struct inode * dir = buf->file->f_inode; - d_entry_preliminary(dir->i_dentry, &qname, ino); - } -#endif + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); dirent = buf->current_dir; - copy_to_user(dirent->d_name, name, namlen); - put_user(0, dirent->d_name + namlen); -#ifdef CONFIG_TRANS_NAMES - { - char * cut; -#ifdef CONFIG_TRANS_RESTRICT - cut = testname(buf->restricted, dirent->d_name); -#else - cut = testname(1, dirent->d_name); -#endif - if(cut) { - int newlen = (int)cut - (int)dirent->d_name; - int newreclen = ROUND_UP(NAME_OFFSET(dirent) + newlen + 1); - /* Either both must fit or none. This way we need - * no status information in f_pos */ - if (reclen+newlen > buf->count) - return -EINVAL; - put_user(0, cut); - put_user(ino, &dirent->d_ino); - put_user(newreclen, &dirent->d_reclen); - put_user(offset, &dirent->d_off); - ((char *) dirent) += newreclen; - buf->count -= newreclen; - put_user(offset, &dirent->d_off); - copy_to_user(dirent->d_name, name, namlen); - put_user(0, dirent->d_name + namlen); - } - } -#endif + buf->previous = dirent; put_user(ino, &dirent->d_ino); put_user(reclen, &dirent->d_reclen); - if (buf->previous) - put_user(buf->file->f_pos, &buf->previous->d_off); - buf->previous = dirent; + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); ((char *) dirent) += reclen; buf->current_dir = dirent; buf->count -= reclen; @@ -206,84 +141,45 @@ static int filldir(void * __buf, const char * name, int namlen, off_t offset, in asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count) { struct file * file; + struct dentry * dentry; + struct inode * inode; + struct linux_dirent * lastdirent; struct getdents_callback buf; - int error = -EBADF; + int error; lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + error = -EBADF; + if (fd >= NR_OPEN) goto out; - error = -ENOTDIR; - if (!file->f_op || !file->f_op->readdir) + + file = current->files->fd[fd]; + if (!file) goto out; - error = verify_area(VERIFY_WRITE, dirent, count); - if (error) + + dentry = file->f_dentry; + if (!dentry) goto out; - buf.file = file; + + inode = dentry->d_inode; + if (!inode) + goto out; + buf.current_dir = (struct linux_dirent *) dirent; buf.previous = NULL; buf.count = count; buf.error = 0; - buf.restricted = 0; -#ifdef CONFIG_TRANS_RESTRICT - buf.restricted = file->f_inode && file->f_inode->i_gid != CONFIG_TRANS_GID; -#endif - buf.do_preload = 0; -#ifdef CONFIG_DCACHE_PRELOAD - if(file->f_inode && file->f_inode->i_dentry && - !(file->f_inode->i_sb->s_type->fs_flags & (FS_NO_DCACHE|FS_NO_PRELIM)) && - !(file->f_inode->i_dentry->d_flag & D_PRELOADED)) - buf.do_preload = 1; -#endif - - if(!(file->f_pos & BASKET_BIT)) { - int oldcount; - do { - oldcount = buf.count; - error = file->f_op->readdir(file->f_inode, file, &buf, filldir); - if (error < 0) - goto out; - } while(!buf.error && buf.count != oldcount); - } - if(!buf.error) { - int nr = 0; - struct dentry * list = file->f_inode ? - d_basket(file->f_inode->i_dentry) : NULL; - struct dentry * ptr = list; -#ifdef CONFIG_DCACHE_PRELOAD - if(buf.do_preload) { - buf.do_preload = 0; - file->f_inode->i_dentry->d_flag |= D_PRELOADED; - } -#endif - if(ptr) { - if(!(file->f_pos & BASKET_BIT)) - file->f_pos = BASKET_BIT; - do { - struct dentry * next = ptr->d_basket_next; - struct inode * inode; - /* vfs_locks() are missing here */ - inode = d_inode(&ptr); - if(inode) { - nr++; - if(nr > (file->f_pos & ~BASKET_BIT)) { - int err = filldir(&buf, ptr->d_name, - ptr->d_len, - file->f_pos, - inode->i_ino); - if(err) - break; - file->f_pos++; - } - iput(inode); - } - ptr = next; - } while(ptr != list); - } - } - if (!buf.previous) { - error = buf.error; - } else { - put_user(file->f_pos, &buf.previous->d_off); + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = file->f_op->readdir(inode, file, &buf, filldir); + if (error < 0) + goto out; + lastdirent = buf.previous; + error = buf.error; + if (lastdirent) { + put_user(file->f_pos, &lastdirent->d_off); error = count - buf.count; } out: diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index 0ddda855d..5d936ee59 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -584,7 +584,7 @@ static struct super_operations romfs_ops = { static struct file_system_type romfs_fs_type = { "romfs", - (FS_REQUIRES_DEV | FS_NO_DCACHE), /* Can dcache be used? */ + FS_REQUIRES_DEV, romfs_read_super, NULL }; diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 20738b0d2..75d3dbff7 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -431,7 +431,7 @@ int smb_current_vmalloced; static struct file_system_type smb_fs_type = { "smbfs", - FS_NO_DCACHE, + 0 /* FS_NO_DCACHE doesn't work correctly */, smb_read_super, NULL }; diff --git a/fs/smbfs/mmap.c b/fs/smbfs/mmap.c index 472fad6de..20a2d0569 100644 --- a/fs/smbfs/mmap.c +++ b/fs/smbfs/mmap.c @@ -119,8 +119,8 @@ smb_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma) inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); vma->vm_ops = &smb_file_mmap; return 0; } @@ -48,8 +48,6 @@ static int cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf) tmp.st_gid = inode->i_gid; tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); tmp.st_size = inode->i_size; - if (inode->i_pipe) - tmp.st_size = PIPE_SIZE(*inode); tmp.st_atime = inode->i_atime; tmp.st_mtime = inode->i_mtime; tmp.st_ctime = inode->i_ctime; @@ -72,8 +70,6 @@ static int cp_new_stat(struct inode * inode, struct stat * statbuf) tmp.st_gid = inode->i_gid; tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); tmp.st_size = inode->i_size; - if (inode->i_pipe) - tmp.st_size = PIPE_SIZE(*inode); tmp.st_atime = inode->i_atime; tmp.st_mtime = inode->i_mtime; tmp.st_ctime = inode->i_ctime; @@ -116,6 +112,7 @@ static int cp_new_stat(struct inode * inode, struct stat * statbuf) return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } + #if !defined(__alpha__) && !defined(__sparc__) /* * For backward compatibility? Maybe this should be moved @@ -123,17 +120,21 @@ static int cp_new_stat(struct inode * inode, struct stat * statbuf) */ asmlinkage int sys_stat(char * filename, struct __old_kernel_stat * statbuf) { - struct inode * inode; + struct dentry * dentry; int error; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) - goto out; - if ((error = do_revalidate(inode)) == 0) - error = cp_old_stat(inode,statbuf); - iput(inode); -out: + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + error = do_revalidate(inode); + if (!error) + error = cp_old_stat(inode, statbuf); + + dput(dentry); + } unlock_kernel(); return error; } @@ -141,17 +142,21 @@ out: asmlinkage int sys_newstat(char * filename, struct stat * statbuf) { - struct inode * inode; + struct dentry * dentry; int error; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) - goto out; - if ((error = do_revalidate(inode)) == 0) - error = cp_new_stat(inode,statbuf); - iput(inode); -out: + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + error = do_revalidate(inode); + if (!error) + error = cp_new_stat(inode,statbuf); + + dput(dentry); + } unlock_kernel(); return error; } @@ -164,17 +169,21 @@ out: */ asmlinkage int sys_lstat(char * filename, struct __old_kernel_stat * statbuf) { - struct inode * inode; + struct dentry * dentry; int error; lock_kernel(); - error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode); - if (error) - goto out; - if ((error = do_revalidate(inode)) == 0) - error = cp_old_stat(inode,statbuf); - iput(inode); -out: + dentry = lnamei(filename); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + error = do_revalidate(inode); + if (!error) + error = cp_old_stat(inode, statbuf); + + dput(dentry); + } unlock_kernel(); return error; } @@ -183,17 +192,21 @@ out: asmlinkage int sys_newlstat(char * filename, struct stat * statbuf) { - struct inode * inode; + struct dentry * dentry; int error; lock_kernel(); - error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode); - if (error) - goto out; - if ((error = do_revalidate(inode)) == 0) - error = cp_new_stat(inode,statbuf); - iput(inode); -out: + dentry = lnamei(filename); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + error = do_revalidate(inode); + if (!error) + error = cp_new_stat(inode,statbuf); + + dput(dentry); + } unlock_kernel(); return error; } @@ -207,17 +220,19 @@ out: asmlinkage int sys_fstat(unsigned int fd, struct __old_kernel_stat * statbuf) { struct file * f; - struct inode * inode; - int ret = -EBADF; + int err = -EBADF; lock_kernel(); - if (fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode)) - goto out; - if ((ret = do_revalidate(inode)) == 0) - ret = cp_old_stat(inode,statbuf); -out: + if (fd < NR_OPEN && (f = current->files->fd[fd]) != NULL) { + struct dentry * dentry = f->f_dentry; + struct inode * inode = dentry->d_inode; + + err = do_revalidate(inode); + if (!err) + err = cp_old_stat(inode,statbuf); + } unlock_kernel(); - return ret; + return err; } #endif @@ -225,45 +240,43 @@ out: asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf) { struct file * f; - struct inode * inode; int err = -EBADF; lock_kernel(); - if (fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode)) - goto out; - if ((err = do_revalidate(inode)) == 0) - err = cp_new_stat(inode,statbuf); -out: + if (fd < NR_OPEN && (f = current->files->fd[fd]) != NULL) { + struct dentry * dentry = f->f_dentry; + struct inode * inode = dentry->d_inode; + + err = do_revalidate(inode); + if (!err) + err = cp_new_stat(inode,statbuf); + } unlock_kernel(); return err; } asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz) { - struct inode * inode; - int error = -EINVAL; + struct dentry * dentry; + int error; - lock_kernel(); if (bufsiz <= 0) - goto out; - error = verify_area(VERIFY_WRITE,buf,bufsiz); - if (error) - goto out; - error = namei(NAM_FOLLOW_TRAILSLASH, path, &inode); - if (error) - goto out; - error = -EINVAL; - if (!inode->i_op || !inode->i_op->readlink || - !S_ISLNK(inode->i_mode) || (error = do_revalidate(inode)) < 0) { - iput(inode); - goto out; - } - if (!IS_RDONLY(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; + return -EINVAL; + + lock_kernel(); + dentry = lnamei(path); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + + error = -EINVAL; + if (inode->i_op && inode->i_op->readlink && !(error = do_revalidate(inode))) { + UPDATE_ATIME(inode); + error = inode->i_op->readlink(inode,buf,bufsiz); + } + dput(dentry); } - error = inode->i_op->readlink(inode,buf,bufsiz); -out: unlock_kernel(); return error; } diff --git a/fs/super.c b/fs/super.c index ec47301aa..f143f9348 100644 --- a/fs/super.c +++ b/fs/super.c @@ -33,7 +33,6 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/fd.h> -#include <linux/dalloc.h> #include <linux/init.h> #include <asm/system.h> @@ -102,13 +101,13 @@ struct vfsmount *add_vfsmnt(kdev_t dev, const char *dev_name, const char *dir_na lptr->mnt_dev = dev; sema_init(&lptr->mnt_sem, 1); - if (dev_name && !getname(dev_name, &tmp)) { + if (dev_name && !IS_ERR(tmp = getname(dev_name))) { if ((lptr->mnt_devname = (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL) strcpy(lptr->mnt_devname, tmp); putname(tmp); } - if (dir_name && !getname(dir_name, &tmp)) { + if (dir_name && !IS_ERR(tmp = getname(dir_name))) { if ((lptr->mnt_dirname = (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL) strcpy(lptr->mnt_dirname, tmp); @@ -198,9 +197,11 @@ static int fs_index(const char * __name) char * name; int err, index; - err = getname(__name, &name); - if (err) + name = getname(__name); + err = PTR_ERR(name); + if (IS_ERR(name)) return err; + index = 0; for (tmp = file_systems ; tmp ; tmp = tmp->next) { if (strcmp(tmp->name, name) == 0) { @@ -454,26 +455,6 @@ struct super_block * get_super(kdev_t dev) return NULL; } -void put_super(kdev_t dev) -{ - struct super_block * sb; - - if (dev == ROOT_DEV) { - printk("VFS: Root device %s: prepare for armageddon\n", - kdevname(dev)); - return; - } - if (!(sb = get_super(dev))) - return; - if (sb->s_covered) { - printk("VFS: Mounted device %s - tssk, tssk\n", - kdevname(dev)); - return; - } - if (sb->s_op && sb->s_op->put_super) - sb->s_op->put_super(sb); -} - asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf) { struct super_block *s; @@ -535,7 +516,6 @@ static struct super_block * read_super(kdev_t dev,const char *name,int flags, return NULL; } s->s_dev = dev; - s->s_covered = NULL; s->s_rd_only = 0; s->s_dirt = 0; s->s_type = type; @@ -570,18 +550,45 @@ void put_unnamed_dev(kdev_t dev) kdevname(dev)); } +static void d_umount(struct dentry *dentry) +{ + struct dentry * covers = dentry->d_covers; + + if (covers != dentry) { + covers->d_mounts = covers; + dentry->d_covers = dentry; + dput(covers); + dput(dentry); + } +} + +static void d_mount(struct dentry *covers, struct dentry *dentry) +{ + if (covers->d_mounts != covers) { + printk("VFS: mount - already mounted\n"); + return; + } + covers->d_mounts = dentry; + dentry->d_covers = covers; +} + static int do_umount(kdev_t dev,int unmount_root) { struct super_block * sb; int retval; + sb = get_super(dev); + if (!sb) + return -ENOENT; + + if (!sb->s_root) + return -ENOENT; + if (dev==ROOT_DEV && !unmount_root) { /* * Special case for "unmounting" root. We just try to remount * it readonly, and sync() the device. */ - if (!(sb=get_super(dev))) - return -ENOENT; if (!(sb->s_flags & MS_RDONLY)) { /* * Make sure all quotas are turned off on this device we need to mount @@ -597,35 +604,51 @@ static int do_umount(kdev_t dev,int unmount_root) } return 0; } - if (!(sb=get_super(dev)) || !(sb->s_covered)) - return -ENOENT; - if (!sb->s_covered->i_mount) - printk("VFS: umount(%s): mounted inode has i_mount=NULL\n", - kdevname(dev)); - while(sb->s_ibasket) - free_ibasket(sb); - if(sb->s_mounted->i_dentry) - d_del(sb->s_mounted->i_dentry, D_NO_CLEAR_INODE); + /* * Before checking if the filesystem is still busy make sure the kernel * doesn't hold any quotafiles open on that device. If the umount fails * too bad there are no quotas running anymore. Turn them on again by hand. */ quota_off(dev, -1); - if (!fs_may_umount(dev, sb->s_mounted)) + if (!fs_may_umount(sb, sb->s_root)) return -EBUSY; - sb->s_covered->i_mount = NULL; - iput(sb->s_covered); - sb->s_covered = NULL; - iput(sb->s_mounted); - sb->s_mounted = NULL; - if (sb->s_op && sb->s_op->write_super && sb->s_dirt) - sb->s_op->write_super(sb); - put_super(dev); + + /* clean up dcache .. */ + d_umount(sb->s_root); + sb->s_root = NULL; + + if (sb->s_op) { + if (sb->s_op->write_super && sb->s_dirt) + sb->s_op->write_super(sb); + if (sb->s_op->put_super) + sb->s_op->put_super(sb); + } remove_vfsmnt(dev); return 0; } +static int umount_dev(kdev_t dev) +{ + int retval; + struct inode * inode = get_empty_inode(); + + inode->i_rdev = dev; + if (MAJOR(dev) >= MAX_BLKDEV) + return -ENXIO; + + retval = do_umount(dev,0); + if (!retval) { + fsync_dev(dev); + if (dev != ROOT_DEV) { + blkdev_release(inode); + put_unnamed_dev(dev); + } + } + iput(inode); + return retval; +} + /* * Now umount can handle mount points as well as block devices. * This is important for filesystems which use unnamed block devices. @@ -639,55 +662,36 @@ static int do_umount(kdev_t dev,int unmount_root) asmlinkage int sys_umount(char * name) { - struct inode * inode; - kdev_t dev; - struct inode * dummy_inode = NULL; - int retval = -EPERM; + struct dentry * dentry; + int retval; - lock_kernel(); if (!suser()) - goto out; - retval = namei(NAM_FOLLOW_LINK, name, &inode); - if (retval) { - retval = namei(NAM_FOLLOW_TRAILSLASH, name, &inode); - if (retval) - goto out; - } - if (S_ISBLK(inode->i_mode)) { - dev = inode->i_rdev; - retval = -EACCES; - if (IS_NODEV(inode)) { - iput(inode); - goto out; - } - } else { - retval = -EINVAL; - if (!inode->i_sb || inode != inode->i_sb->s_mounted) { - iput(inode); - goto out; - } - dev = inode->i_sb->s_dev; - iput(inode); - inode = dummy_inode = get_empty_inode(); - inode->i_rdev = dev; - } - retval = -ENXIO; - if (MAJOR(dev) >= MAX_BLKDEV) { - iput(inode); - goto out; - } - retval = do_umount(dev,0); - if (!retval) { - fsync_dev(dev); - if (dev != ROOT_DEV) { - blkdev_release (inode); - put_unnamed_dev(dev); + return -EPERM; + + lock_kernel(); + dentry = namei(name); + retval = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + kdev_t dev = inode->i_rdev; + + retval = 0; + if (S_ISBLK(inode->i_mode)) { + if (IS_NODEV(inode)) + retval = -EACCES; + } else { + struct super_block *sb = inode->i_sb; + retval = -EINVAL; + if (sb && inode == sb->s_root->d_inode) { + dev = sb->s_dev; + retval = 0; + } } + dput(dentry); + + if (!retval) + retval = umount_dev(dev); } - iput(inode); - if (!retval) - fsync_dev(dev); -out: unlock_kernel(); return retval; } @@ -715,45 +719,39 @@ out: int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const char * type, int flags, void * data) { - struct inode * dir_i = NULL; + struct dentry * dir_d = NULL; struct super_block * sb; struct vfsmount *vfsmnt; int error; - int override = 0; - - if(dir_name) { - char c; - get_user(c, dir_name); - override = (c == '!'); - } if (!(flags & MS_RDONLY) && dev && is_read_only(dev)) return -EACCES; /*flags |= MS_RDONLY;*/ - if(override) - dir_name++; - error = namei(NAM_FOLLOW_LINK, dir_name, &dir_i); - if (error) + + dir_d = namei(dir_name); + error = PTR_ERR(dir_d); + if (IS_ERR(dir_d)) return error; - if (!override && (atomic_read(&dir_i->i_count) != 1 || dir_i->i_mount)) { - iput(dir_i); + + if (dir_d->d_covers != dir_d) { + dput(dir_d); return -EBUSY; } - if (!S_ISDIR(dir_i->i_mode)) { - iput(dir_i); + if (!S_ISDIR(dir_d->d_inode->i_mode)) { + dput(dir_d); return -ENOTDIR; } - if (!fs_may_mount(dev) && !override) { - iput(dir_i); + if (!fs_may_mount(dev)) { + dput(dir_d); return -EBUSY; } sb = read_super(dev,type,flags,data,0); if (!sb) { - iput(dir_i); + dput(dir_d); return -EINVAL; } - if (sb->s_covered) { - iput(dir_i); + if (sb->s_root->d_covers != sb->s_root) { + dput(dir_d); return -EBUSY; } vfsmnt = add_vfsmnt(dev, dev_name, dir_name); @@ -761,25 +759,8 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha vfsmnt->mnt_sb = sb; vfsmnt->mnt_flags = flags; } - { - struct dentry * old = dir_i->i_dentry; - struct dentry * new; - vfs_lock(); - new = d_alloc(old->d_parent, old->d_len, 1); - if(new) { - struct qstr copy = { old->d_name, old->d_len }; - d_add(new, sb->s_mounted, ©, D_DUPLICATE); - vfs_unlock(); - } else { - printk("VFS: cannot setup dentry for mount\n"); - iput(dir_i); - return -ENOMEM; - } - vfs_unlock(); - } - sb->s_covered = dir_i; - dir_i->i_mount = sb->s_mounted; - return 0; /* we don't iput(dir_i) - see umount */ + d_mount(dir_d, sb->s_root); + return 0; /* we don't dput(dir) - see umount */ } @@ -799,7 +780,7 @@ static int do_remount_sb(struct super_block *sb, int flags, char *data) /*flags |= MS_RDONLY;*/ /* If we are remounting RDONLY, make sure there are no rw files open */ if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY)) - if (!fs_may_remount_ro(sb->s_dev)) + if (!fs_may_remount_ro(sb)) return -EBUSY; sb->s_flags = (flags & ~MS_RDONLY) | (sb->s_flags & MS_RDONLY); if (sb->s_op && sb->s_op->remount_fs) { @@ -817,18 +798,19 @@ static int do_remount_sb(struct super_block *sb, int flags, char *data) static int do_remount(const char *dir,int flags,char *data) { - struct inode *dir_i; + struct dentry *dentry; int retval; - retval = namei(NAM_FOLLOW_LINK, dir, &dir_i); - if (retval) - return retval; - if (dir_i != dir_i->i_sb->s_mounted) { - iput(dir_i); - return -EINVAL; + dentry = namei(dir); + retval = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct super_block * sb = dentry->d_inode->i_sb; + + retval = -EINVAL; + if (dentry == sb->s_root) + retval = do_remount_sb(sb, flags, data); + dput(dentry); } - retval = do_remount_sb(dir_i->i_sb, flags, data); - iput(dir_i); return retval; } @@ -879,7 +861,8 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, unsigned long new_flags, void * data) { struct file_system_type * fstype; - struct inode * inode; + struct dentry * dentry = NULL; + struct inode * inode = NULL; struct file_operations * fops; kdev_t dev; int retval = -EPERM; @@ -911,58 +894,54 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, goto out; t = fstype->name; fops = NULL; - if ((fstype->fs_flags & FS_REQUIRES_DEV)) { - retval = namei(NAM_FOLLOW_LINK, dev_name, &inode); - if (retval) + if (fstype->fs_flags & FS_REQUIRES_DEV) { + dentry = namei(dev_name); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + + inode = dentry->d_inode; retval = -ENOTBLK; - if (!S_ISBLK(inode->i_mode)) { - iput(inode); - goto out; - } + if (!S_ISBLK(inode->i_mode)) + goto dput_and_out; + retval = -EACCES; - if (IS_NODEV(inode)) { - iput(inode); - goto out; - } + if (IS_NODEV(inode)) + goto dput_and_out; + dev = inode->i_rdev; retval = -ENXIO; - if (MAJOR(dev) >= MAX_BLKDEV) { - iput(inode); - goto out; - } + if (MAJOR(dev) >= MAX_BLKDEV) + goto dput_and_out; + fops = get_blkfops(MAJOR(dev)); retval = -ENOTBLK; - if (!fops) { - iput(inode); - goto out; - } + if (!fops) + goto dput_and_out; + if (fops->open) { struct file dummy; /* allows read-write or read-only flag */ memset(&dummy, 0, sizeof(dummy)); - dummy.f_inode = inode; + dummy.f_dentry = dentry; dummy.f_mode = (new_flags & MS_RDONLY) ? 1 : 3; retval = fops->open(inode, &dummy); - if (retval) { - iput(inode); - goto out; - } + if (retval) + goto dput_and_out; } } else { retval = -EMFILE; if (!(dev = get_unnamed_dev())) goto out; - inode = NULL; } + page = 0; if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) { flags = new_flags & ~MS_MGC_MSK; retval = copy_mount_options(data, &page); if (retval < 0) { put_unnamed_dev(dev); - iput(inode); - goto out; + goto dput_and_out; } } retval = do_mount(dev,dev_name,dir_name,t,flags,(void *) page); @@ -971,7 +950,8 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, fops->release(inode, NULL); put_unnamed_dev(dev); } - iput(inode); +dput_and_out: + dput(dentry); out: unlock_kernel(); return retval; @@ -982,7 +962,7 @@ __initfunc(static void do_mount_root(void)) struct file_system_type * fs_type; struct super_block * sb; struct vfsmount *vfsmnt; - struct inode * inode, * d_inode = NULL; + struct inode * d_inode = NULL; struct file filp; int retval; @@ -1001,15 +981,11 @@ __initfunc(static void do_mount_root(void)) sb->s_dev = get_unnamed_dev(); sb->s_flags = root_mountflags & ~MS_RDONLY; if (nfs_root_mount(sb) >= 0) { - inode = sb->s_mounted; - atomic_add(3, &inode->i_count); - sb->s_covered = inode; sb->s_rd_only = 0; sb->s_dirt = 0; sb->s_type = fs_type; - current->fs->pwd = inode; - current->fs->root = inode; - (void)d_alloc_root(inode); + current->fs->root = dget(sb->s_root); + current->fs->pwd = dget(sb->s_root); ROOT_DEV = sb->s_dev; printk (KERN_NOTICE "VFS: Mounted root (nfs filesystem).\n"); vfsmnt = add_vfsmnt(ROOT_DEV, "/dev/root", "/"); @@ -1042,7 +1018,7 @@ __initfunc(static void do_mount_root(void)) memset(&filp, 0, sizeof(filp)); d_inode = get_empty_inode(); d_inode->i_rdev = ROOT_DEV; - filp.f_inode = d_inode; + filp.f_dentry = NULL; if ( root_mountflags & MS_RDONLY) filp.f_mode = 1; /* read only */ else @@ -1066,15 +1042,9 @@ __initfunc(static void do_mount_root(void)) continue; sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1); if (sb) { - inode = sb->s_mounted; - - /* NOTE! it is logically used 4 times, not 1 */ - atomic_add(3, &inode->i_count); - sb->s_covered = inode; sb->s_flags = root_mountflags; - current->fs->pwd = inode; - current->fs->root = inode; - (void)d_alloc_root(inode); + current->fs->root = dget(sb->s_root); + current->fs->pwd = dget(sb->s_root); printk ("VFS: Mounted root (%s filesystem)%s.\n", fs_type->name, (sb->s_flags & MS_RDONLY) ? " readonly" : ""); @@ -1106,8 +1076,7 @@ __initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old)) { kdev_t old_root_dev; struct vfsmount *vfsmnt; - struct inode *old_root,*old_pwd,*inode; - unsigned long old_fs; + struct dentry *old_root,*old_pwd,*dir_d = NULL; int error; old_root = current->fs->root; @@ -1119,24 +1088,29 @@ __initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old)) } ROOT_DEV = new_root_dev; do_mount_root(); - old_fs = get_fs(); - set_fs(get_ds()); - error = namei(NAM_FOLLOW_LINK, put_old, &inode); - if (error) inode = NULL; - set_fs(old_fs); - if (!error && (atomic_read(&inode->i_count) != 1 || inode->i_mount)) + dir_d = lookup_dentry(put_old, NULL, 1); + if (IS_ERR(dir_d)) { + error = PTR_ERR(dir_d); + } else if (!dir_d->d_inode) { + dput(dir_d); + error = -ENOENT; + } else { + error = 0; + } + if (!error && dir_d->d_covers != dir_d) { + dput(dir_d); error = -EBUSY; - if (!error && !S_ISDIR(inode->i_mode)) + } + if (!error && !S_ISDIR(dir_d->d_inode->i_mode)) { + dput(dir_d); error = -ENOTDIR; - iput(old_root); /* current->fs->root */ - iput(old_pwd); /* current->fs->pwd */ + } + dput(old_root); + dput(old_pwd); if (error) { int umount_error; - if (inode) iput(inode); printk(KERN_NOTICE "Trying to unmount old root ... "); - old_root->i_mount = old_root; - /* does this belong into do_mount_root ? */ umount_error = do_umount(old_root_dev,1); if (umount_error) printk(KERN_ERR "error %d\n",umount_error); else { @@ -1145,16 +1119,16 @@ __initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old)) } return umount_error ? error : 0; } - iput(old_root); /* sb->s_covered */ remove_vfsmnt(old_root_dev); vfsmnt = add_vfsmnt(old_root_dev,"/dev/root.old",put_old); if (!vfsmnt) printk(KERN_CRIT "Trouble: add_vfsmnt failed\n"); else { - vfsmnt->mnt_sb = old_root->i_sb; - vfsmnt->mnt_sb->s_covered = inode; + vfsmnt->mnt_sb = old_root->d_inode->i_sb; + d_mount(dir_d,vfsmnt->mnt_sb->s_root); vfsmnt->mnt_flags = vfsmnt->mnt_sb->s_flags; } - inode->i_mount = old_root; + d_umount(old_root); + d_mount(dir_d,old_root); return 0; } diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index 8b942a5b1..3dd0931cf 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -57,6 +57,7 @@ struct inode_operations sysv_dir_inode_operations = { sysv_mknod, /* mknod */ sysv_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/sysv/file.c b/fs/sysv/file.c index da07ef7a8..1a28e4c2a 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -64,6 +64,7 @@ struct inode_operations sysv_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ sysv_bmap, /* bmap */ @@ -194,7 +195,7 @@ long sysv_file_read(struct inode * inode, struct file * filp, filp->f_reada = 1; if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } return read; } @@ -255,7 +256,7 @@ static long sysv_file_write(struct inode * inode, struct file * filp, pos += c; if (pos > inode->i_size) { inode->i_size = pos; - inode->i_dirt = 1; + mark_inode_dirty(inode); } written += c; buf += c; @@ -265,6 +266,6 @@ static long sysv_file_write(struct inode * inode, struct file * filp, } inode->i_mtime = inode->i_ctime = CURRENT_TIME; filp->f_pos = pos; - inode->i_dirt = 1; + mark_inode_dirty(inode); return written; } diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c index 97bc7284f..fa0b3cf95 100644 --- a/fs/sysv/ialloc.c +++ b/fs/sysv/ialloc.c @@ -62,9 +62,8 @@ void sysv_free_inode(struct inode * inode) printk("sysv_free_inode: inode has no device\n"); return; } - if (atomic_read(&inode->i_count) != 1) { - printk("sysv_free_inode: inode has count=%d\n", - atomic_read(&inode->i_count)); + if (inode->i_count != 1) { + printk("sysv_free_inode: inode has count=%d\n", inode->i_count); return; } if (inode->i_nlink) { @@ -150,12 +149,12 @@ struct inode * sysv_new_inode(const struct inode * dir) mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */ if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1); sb->s_dirt = 1; /* and needs time stamp */ - atomic_set(&inode->i_count, 1); + inode->i_count = 1; inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; - inode->i_dirt = 1; + mark_inode_dirty(inode); inode->i_ino = ino; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_op = NULL; @@ -166,7 +165,7 @@ struct inode * sysv_new_inode(const struct inode * dir) inode->i_size = 0; /* ditto */ sysv_write_inode(inode); /* ensure inode not allocated again */ /* FIXME: caller may call this too. */ - inode->i_dirt = 1; /* cleared by sysv_write_inode() */ + mark_inode_dirty(inode); /* cleared by sysv_write_inode() */ /* That's it. */ (*sb->sv_sb_total_free_inodes)--; mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified again */ diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index f8c6a1b38..b2d7edfbc 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -344,6 +344,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, struct buffer_head *bh; const char *found; kdev_t dev = sb->s_dev; + struct inode *root_inode; if (1024 != sizeof (struct xenix_super_block)) panic("Xenix FS: bad super-block size"); @@ -483,9 +484,10 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, /* set up enough so that it can read an inode */ sb->s_dev = dev; sb->s_op = &sysv_sops; - sb->s_mounted = iget(sb,SYSV_ROOT_INO); + root_inode = iget(sb,SYSV_ROOT_INO); + sb->s_root = d_alloc_root(root_inode, NULL); unlock_super(sb); - if (!sb->s_mounted) { + if (!sb->s_root) { printk("SysV FS: get root inode failed\n"); sysv_put_super(sb); return NULL; @@ -534,7 +536,7 @@ void sysv_put_super(struct super_block *sb) MOD_DEC_USE_COUNT; } -void sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +int sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; @@ -547,7 +549,7 @@ void sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) tmp.f_ffree = sysv_count_free_inodes(sb); /* free file nodes in fs */ tmp.f_namelen = SYSV_NAMELEN; /* Don't know what value to put in tmp.f_fsid */ /* file system id */ - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } @@ -667,7 +669,7 @@ repeat: } *p = tmp; inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); return result; } @@ -900,13 +902,11 @@ static struct buffer_head * sysv_update_inode(struct inode * inode) printk("Bad inode number on dev %s" ": %d is out of range\n", kdevname(inode->i_dev), ino); - inode->i_dirt = 0; return 0; } block = sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits); if (!(bh = sv_bread(sb,inode->i_dev,block))) { printk("unable to read i-node block\n"); - inode->i_dirt = 0; return 0; } raw_inode = (struct sysv_inode *) bh->b_data + ((ino-1) & sb->sv_inodes_per_block_1); @@ -937,7 +937,6 @@ static struct buffer_head * sysv_update_inode(struct inode * inode) else for (block = 0; block < 10+1+1+1; block++) write3byte(&raw_inode->i_a.i_addb[3*block],inode->u.sysv_i.i_data[block]); - inode->i_dirt=0; mark_buffer_dirty(bh, 1); return bh; } diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index d1b67ab5f..b3586b58f 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -175,7 +175,7 @@ static int sysv_add_entry(struct inode * dir, if (pos > dir->i_size) { de->inode = 0; dir->i_size = pos; - dir->i_dirt = 1; + mark_inode_dirty(dir); } if (de->inode) { if (namecompare(namelen, SYSV_NAMELEN, name, de->name)) { @@ -184,7 +184,7 @@ static int sysv_add_entry(struct inode * dir, } } else { dir->i_mtime = dir->i_ctime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); for (i = 0; i < SYSV_NAMELEN ; i++) de->name[i] = (i < namelen) ? name[i] : 0; mark_buffer_dirty(bh, 1); @@ -219,11 +219,11 @@ int sysv_create(struct inode * dir,const char * name, int len, int mode, } inode->i_op = &sysv_file_inode_operations; inode->i_mode = mode; - inode->i_dirt = 1; + mark_inode_dirty(inode); error = sysv_add_entry(dir,name,len, &bh ,&de); if (error) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); iput(dir); return error; @@ -276,11 +276,11 @@ int sysv_mknod(struct inode * dir, const char * name, int len, int mode, int rde init_fifo(inode); if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = to_kdev_t(rdev); - inode->i_dirt = 1; + mark_inode_dirty(inode); error = sysv_add_entry(dir, name, len, &bh, &de); if (error) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); iput(dir); return error; @@ -325,7 +325,7 @@ int sysv_mkdir(struct inode * dir, const char * name, int len, int mode) if (!dir_block) { iput(dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); return -ENOSPC; } @@ -341,7 +341,7 @@ int sysv_mkdir(struct inode * dir, const char * name, int len, int mode) inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask); if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; - inode->i_dirt = 1; + mark_inode_dirty(inode); error = sysv_add_entry(dir, name, len, &bh, &de); if (error) { iput(dir); @@ -352,7 +352,7 @@ int sysv_mkdir(struct inode * dir, const char * name, int len, int mode) de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); dir->i_nlink++; - dir->i_dirt = 1; + mark_inode_dirty(dir); iput(dir); iput(inode); brelse(bh); @@ -454,7 +454,7 @@ int sysv_rmdir(struct inode * dir, const char * name, int len) retval = -ENOENT; goto end_rmdir; } - if (atomic_read(&inode->i_count) > 1) { + if (inode->i_count > 1) { retval = -EBUSY; goto end_rmdir; } @@ -463,10 +463,10 @@ int sysv_rmdir(struct inode * dir, const char * name, int len) de->inode = 0; mark_buffer_dirty(bh, 1); inode->i_nlink=0; - inode->i_dirt=1; + mark_inode_dirty(inode); dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt=1; + mark_inode_dirty(dir); retval = 0; end_rmdir: iput(dir); @@ -517,10 +517,10 @@ repeat: de->inode = 0; mark_buffer_dirty(bh, 1); dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); inode->i_nlink--; inode->i_ctime = dir->i_ctime; - inode->i_dirt = 1; + mark_inode_dirty(inode); retval = 0; end_unlink: brelse(bh); @@ -550,7 +550,7 @@ int sysv_symlink(struct inode * dir, const char * name, int len, const char * sy if (!name_block) { iput(dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); return -ENOSPC; } @@ -563,11 +563,11 @@ int sysv_symlink(struct inode * dir, const char * name, int len, const char * sy mark_buffer_dirty(name_block, 1); brelse(name_block); inode->i_size = i; - inode->i_dirt = 1; + mark_inode_dirty(inode); bh = sysv_find_entry(dir,name,len,&de); if (bh) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); brelse(bh); iput(dir); @@ -576,7 +576,7 @@ int sysv_symlink(struct inode * dir, const char * name, int len, const char * sy i = sysv_add_entry(dir, name, len, &bh, &de); if (i) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); iput(dir); return i; @@ -624,7 +624,7 @@ int sysv_link(struct inode * oldinode, struct inode * dir, const char * name, in iput(dir); oldinode->i_nlink++; oldinode->i_ctime = CURRENT_TIME; - oldinode->i_dirt = 1; + mark_inode_dirty(oldinode); iput(oldinode); return 0; } @@ -635,7 +635,7 @@ static int subdir(struct inode * new_inode, struct inode * old_inode) int ino; int result; - atomic_inc(&new_inode->i_count); + new_inode->i_count++; result = 0; for (;;) { if (new_inode == old_inode) { @@ -667,8 +667,8 @@ static int subdir(struct inode * new_inode, struct inode * old_inode) * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ -static int do_sysv_rename(struct inode * old_dir, const char * old_name, int old_len, - struct inode * new_dir, const char * new_name, int new_len) +static int do_sysv_rename(struct inode * old_dir, struct dentry * old_dentry, + struct inode * new_dir, struct dentry * new_dentry) { struct inode * old_inode, * new_inode; struct buffer_head * old_bh, * new_bh, * dir_bh; @@ -687,21 +687,21 @@ try_again: start_up: old_inode = new_inode = NULL; old_bh = new_bh = dir_bh = NULL; - old_bh = sysv_find_entry(old_dir,old_name,old_len,&old_de); + old_bh = sysv_find_entry(old_dir,old_dentry->d_name.name, + old_dentry->d_name.len,&old_de); retval = -ENOENT; if (!old_bh) goto end_rename; - old_inode = __iget(old_dir->i_sb, old_de->inode, 0); /* don't cross mnt-points */ - if (!old_inode) - goto end_rename; + old_inode = old_dentry->d_inode;/* don't cross mnt-points */ retval = -EPERM; if ((old_dir->i_mode & S_ISVTX) && current->fsuid != old_inode->i_uid && current->fsuid != old_dir->i_uid && !fsuser()) goto end_rename; - new_bh = sysv_find_entry(new_dir,new_name,new_len,&new_de); + new_inode = new_dentry->d_inode; + new_bh = sysv_find_entry(new_dir,new_dentry->d_name.name, + new_dentry->d_name.len,&new_de); if (new_bh) { - new_inode = __iget(new_dir->i_sb, new_de->inode, 0); if (!new_inode) { brelse(new_bh); new_bh = NULL; @@ -722,7 +722,7 @@ start_up: if (!empty_dir(new_inode)) goto end_rename; retval = -EBUSY; - if (atomic_read(&new_inode->i_count) > 1) + if (new_inode->i_count > 1) goto end_rename; } retval = -EPERM; @@ -748,7 +748,8 @@ start_up: goto end_rename; } if (!new_bh) { - retval = sysv_add_entry(new_dir,new_name,new_len,&new_bh,&new_de); + retval = sysv_add_entry(new_dir,new_dentry->d_name.name, + new_dentry->d_name.len,&new_bh,&new_de); if (retval) goto end_rename; } @@ -763,13 +764,13 @@ start_up: old_de->inode = 0; new_de->inode = old_inode->i_ino; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; - old_dir->i_dirt = 1; + mark_inode_dirty(old_dir); new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); } mark_buffer_dirty(old_bh, 1); mark_buffer_dirty(new_bh, 1); @@ -777,13 +778,13 @@ start_up: PARENT_INO(dir_bh->b_data) = new_dir->i_ino; mark_buffer_dirty(dir_bh, 1); old_dir->i_nlink--; - old_dir->i_dirt = 1; + mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink--; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); } else { new_dir->i_nlink++; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); } } retval = 0; @@ -807,8 +808,8 @@ end_rename: * the same device that races occur: many renames can happen at once, as long * as they are on different partitions. */ -int sysv_rename(struct inode * old_dir, const char * old_name, int old_len, - struct inode * new_dir, const char * new_name, int new_len) +int sysv_rename(struct inode * old_dir, struct dentry * old_dentry, + struct inode * new_dir, struct dentry * new_dentry) { static struct wait_queue * wait = NULL; static int lock = 0; @@ -817,8 +818,8 @@ int sysv_rename(struct inode * old_dir, const char * old_name, int old_len, while (lock) sleep_on(&wait); lock = 1; - result = do_sysv_rename(old_dir, old_name, old_len, - new_dir, new_name, new_len); + result = do_sysv_rename(old_dir, old_dentry, + new_dir, new_dentry); lock = 0; wake_up(&wait); return result; diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c index 4e8a5e349..d76c3fa66 100644 --- a/fs/sysv/symlink.c +++ b/fs/sysv/symlink.c @@ -21,6 +21,7 @@ #include <asm/uaccess.h> static int sysv_readlink(struct inode *, char *, int); +static struct dentry *sysv_follow_link(struct inode *, struct dentry *); /* * symlinks can't do much... @@ -37,6 +38,7 @@ struct inode_operations sysv_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ sysv_readlink, /* readlink */ + sysv_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -44,6 +46,21 @@ struct inode_operations sysv_symlink_inode_operations = { NULL /* permission */ }; +static struct dentry *sysv_follow_link(struct inode * inode, struct dentry * base) +{ + struct buffer_head * bh; + + bh = sysv_file_bread(inode, 0, 0); + if (!bh) { + dput(base); + return ERR_PTR(-EIO); + } + UPDATE_ATIME(inode); + base = lookup_dentry(bh->b_data, base, 1); + brelse(bh); + return base; +} + static int sysv_readlink(struct inode * inode, char * buffer, int buflen) { struct buffer_head * bh; @@ -54,7 +71,6 @@ static int sysv_readlink(struct inode * inode, char * buffer, int buflen) if (buflen > inode->i_sb->sv_block_size_1) buflen = inode->i_sb->sv_block_size_1; bh = sysv_file_bread(inode, 0, 0); - iput(inode); if (!bh) return 0; bh_data = bh->b_data; diff --git a/fs/sysv/truncate.c b/fs/sysv/truncate.c index 0eeb10d30..433c39cae 100644 --- a/fs/sysv/truncate.c +++ b/fs/sysv/truncate.c @@ -64,7 +64,7 @@ repeat: continue; } *p = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); brelse(bh); sysv_free_block(sb,block); } @@ -257,12 +257,14 @@ done: static int trunc_all(struct inode * inode) { struct super_block * sb; + char * res; sb = inode->i_sb; + res = (char *)test_bit(I_DIRTY,&inode->i_state); return trunc_direct(inode) - | trunc_indirect(inode,sb->sv_ind0_size,&inode->u.sysv_i.i_data[10],0,&inode->i_dirt) - | trunc_dindirect(inode,sb->sv_ind1_size,&inode->u.sysv_i.i_data[11],0,&inode->i_dirt) - | trunc_tindirect(inode,sb->sv_ind2_size,&inode->u.sysv_i.i_data[12],0,&inode->i_dirt); + | trunc_indirect(inode,sb->sv_ind0_size,&inode->u.sysv_i.i_data[10],0,res) + | trunc_dindirect(inode,sb->sv_ind1_size,&inode->u.sysv_i.i_data[11],0,res) + | trunc_tindirect(inode,sb->sv_ind2_size,&inode->u.sysv_i.i_data[12],0,res); } @@ -285,5 +287,5 @@ void sysv_truncate(struct inode * inode) schedule(); } inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } diff --git a/fs/ufs/ufs_file.c b/fs/ufs/ufs_file.c index 74ae1a470..ef7858c8f 100644 --- a/fs/ufs/ufs_file.c +++ b/fs/ufs/ufs_file.c @@ -6,7 +6,7 @@ * Laboratory for Computer Science Research Computing Facility * Rutgers, The State University of New Jersey * - * $Id: ufs_file.c,v 1.8 1997/06/05 01:29:09 davem Exp $ + * $Id: ufs_file.c,v 1.2 1997/06/17 13:27:28 ralf Exp $ * */ @@ -41,6 +41,7 @@ struct inode_operations ufs_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ ufs_bmap, /* bmap */ diff --git a/fs/ufs/ufs_inode.c b/fs/ufs/ufs_inode.c index f0fdd5d5f..d8c8c6896 100644 --- a/fs/ufs/ufs_inode.c +++ b/fs/ufs/ufs_inode.c @@ -6,7 +6,7 @@ * Laboratory for Computer Science Research Computing Facility * Rutgers, The State University of New Jersey * - * $Id: ufs_inode.c,v 1.8 1997/06/04 08:28:28 davem Exp $ + * $Id: ufs_inode.c,v 1.2 1997/06/17 13:27:29 ralf Exp $ * */ @@ -19,8 +19,7 @@ void ufs_print_inode(struct inode * inode) printk("ino %lu mode 0%6.6o lk %d uid %d gid %d" " sz %lu blks %lu cnt %u\n", inode->i_ino, inode->i_mode, inode->i_nlink, inode->i_uid, - inode->i_gid, inode->i_size, inode->i_blocks, - atomic_read(&inode->i_count)); + inode->i_gid, inode->i_size, inode->i_blocks, inode->i_count); printk(" db <0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x" " 0x%x 0x%x 0x%x 0x%x>\n", inode->u.ufs_i.i_data[0], inode->u.ufs_i.i_data[1], diff --git a/fs/ufs/ufs_namei.c b/fs/ufs/ufs_namei.c index 03ea2dde1..64ea3a866 100644 --- a/fs/ufs/ufs_namei.c +++ b/fs/ufs/ufs_namei.c @@ -6,7 +6,7 @@ * Laboratory for Computer Science Research Computing Facility * Rutgers, The State University of New Jersey * - * $Id: ufs_namei.c,v 1.7 1996/06/01 14:56:49 ecd Exp $ + * $Id: ufs_namei.c,v 1.1.1.1 1997/06/01 03:16:19 ralf Exp $ * */ @@ -35,12 +35,14 @@ static int ufs_match (int len, const char * const name, struct ufs_direct * d) } /* XXX - this is a mess, especially for endianity */ -int ufs_lookup (struct inode * dir, const char * name, int len, +int ufs_lookup (struct inode * dir, struct qstr *qname, struct inode ** result) { unsigned long int lfragno, fragno; struct buffer_head * bh; struct ufs_direct * d; + const char *name = qname->name; + int len = qname->len; if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) printk("Passed name: %s\nPassed length: %d\n", name, len); diff --git a/fs/ufs/ufs_super.c b/fs/ufs/ufs_super.c index 342722237..f08292a23 100644 --- a/fs/ufs/ufs_super.c +++ b/fs/ufs/ufs_super.c @@ -8,7 +8,7 @@ * * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * - * $Id: ufs_super.c,v 1.24 1997/06/04 08:28:29 davem Exp $ + * $Id: ufs_super.c,v 1.2 1997/06/17 13:27:29 ralf Exp $ * */ @@ -254,7 +254,7 @@ ufs_read_super(struct super_block * sb, void * data, int silent) sb->u.ufs_sb.s_lmask = ~((ufs_swab32(usb->fs_fmask) - ufs_swab32(usb->fs_bmask)) >> ufs_swab32(usb->fs_fshift)); sb->u.ufs_sb.s_fsfrag = ufs_swab32(usb->fs_frag); /* XXX - rename this later */ - sb->s_mounted = iget(sb, UFS_ROOTINO); + sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL); #ifdef DEBUG_UFS_SUPER printk("ufs_read_super: inopb %u\n", sb->u.ufs_sb.s_inopb); diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index 161bc791e..b3263b42d 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -877,7 +877,7 @@ static int vfat_find(struct inode *dir,const char *name,int len, PRINTK(("vfat_find: create file 4\n")); dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); PRINTK(("vfat_find: create file 5\n")); @@ -1010,7 +1010,7 @@ static int vfat_create_entry(struct inode *dir,const char *name,int len, return -EIO; (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime = CURRENT_TIME; - (*result)->i_dirt = 1; + mark_inode_dirty(*result); (*result)->i_version = ++event; dir->i_version = event; @@ -1046,7 +1046,7 @@ static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent, * XXX all times should be set by caller upon successful completion. */ dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); memcpy(de->name,name,MSDOS_NAME); memset(de->unused, 0, sizeof(de->unused)); de->lcase = 0; @@ -1062,7 +1062,7 @@ static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent, vfat_read_inode(dot); if (!dot) return -EIO; dot->i_mtime = dot->i_atime = CURRENT_TIME; - dot->i_dirt = 1; + mark_inode_dirty(dot); if (isdot) { dot->i_size = dir->i_size; MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start; @@ -1125,7 +1125,7 @@ static int vfat_empty(struct inode *dir) struct buffer_head *bh; struct msdos_dir_entry *de; - if (atomic_read(&dir->i_count) > 1) + if (dir->i_count > 1) return -EBUSY; if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */ pos = 0; @@ -1173,7 +1173,8 @@ static int vfat_rmdir_free_ino(struct inode *dir,struct buffer_head *bh, inode->i_mtime = dir->i_mtime = CURRENT_TIME; inode->i_atime = dir->i_atime = CURRENT_TIME; dir->i_nlink--; - inode->i_dirt = dir->i_dirt = 1; + mark_inode_dirty(dir); + mark_inode_dirty(inode); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); iput(inode); @@ -1196,7 +1197,8 @@ static int vfat_unlink_free_ino(struct inode *dir,struct buffer_head *bh, inode->i_atime = dir->i_atime = CURRENT_TIME; dir->i_version = ++event; MSDOS_I(inode)->i_busy = 1; - inode->i_dirt = dir->i_dirt = 1; + mark_inode_dirty(dir); + mark_inode_dirty(inode); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); @@ -1478,7 +1480,7 @@ int vfat_rename(struct inode *old_dir,const char *old_name,int old_len, MSDOS_I(new_inode)->i_oldlink = old_inode; fat_cache_inval_inode(old_inode); PRINTK(("vfat_rename 15: old_slots=%d\n",old_slots)); - old_inode->i_dirt = 1; + mark_inode_dirty(old_inode); old_dir->i_version = ++event; /* remove the old entry */ @@ -1511,7 +1513,7 @@ int vfat_rename(struct inode *old_dir,const char *old_name,int old_len, } dotdot_de->start = MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start; - dotdot_inode->i_dirt = 1; + mark_inode_dirty(dotdot_inode); fat_mark_buffer_dirty(sb, dotdot_bh, 1); old_dir->i_nlink--; new_dir->i_nlink++; |