diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
commit | db7d4daea91e105e3859cf461d7e53b9b77454b2 (patch) | |
tree | 9bb65b95440af09e8aca63abe56970dd3360cc57 /fs | |
parent | 9c1c01ead627bdda9211c9abd5b758d6c687d8ac (diff) |
Merge with Linux 2.2.8.
Diffstat (limited to 'fs')
127 files changed, 2814 insertions, 2758 deletions
diff --git a/fs/Config.in b/fs/Config.in index 1fdf8acd7..d9c8c08cd 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -105,10 +105,6 @@ fi endmenu fi -if [ "$CONFIG_AFFS_FS" != "n" ]; then - define_bool CONFIG_AMIGA_PARTITION y -fi - mainmenu_option next_comment comment 'Partition Types' @@ -124,6 +120,10 @@ fi endmenu +if [ "$CONFIG_AFFS_FS" != "n" ]; then + define_bool CONFIG_AMIGA_PARTITION y +fi + source fs/nls/Config.in endmenu diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index 12f7f6f6d..ac81954d7 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -280,8 +280,6 @@ static int adfs_readdir (struct file *filp, void *dirent, filldir_t filldir) unsigned long parent_object_id, dir_object_id; int buffers, pos; - if (!inode || !S_ISDIR(inode->i_mode)) - return -EBADF; sb = inode->i_sb; if (filp->f_pos > ADFS_NUM_DIR_ENTRIES + 2) diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index 37daaa3c3..6c8814567 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -27,7 +27,7 @@ #define inode_dirindex(idx) (((idx) & 0xff) * 26 - 21) #define frag_id(x) (((x) >> 8) & 0x7fff) -#define off(x) (((x) & 0xff) ? ((x) & 0xff) - 1 : 0) +#define off(x) (((x) & 0xff) ? (((x) & 0xff) - 1) << sb->u.adfs_sb.s_dr->log2sharesize : 0) static inline int adfs_inode_validate_no (struct super_block *sb, unsigned int inode_no) { @@ -83,11 +83,20 @@ int adfs_bmap (struct inode *inode, int block) return 0; } + if (block < 0) { + adfs_error(sb, "adfs_bmap", "block(%d) < 0", block); + return 0; + } + + if (block > inode->i_blocks) + return 0; + + block += off(inode->u.adfs_i.file_id); + if (frag_id(inode->u.adfs_i.file_id) == ADFS_ROOT_FRAG) - blk = sb->u.adfs_sb.s_map_block + off(inode_frag (inode->i_ino)) + block; + blk = sb->u.adfs_sb.s_map_block + block; else - blk = adfs_map_lookup (sb, frag_id(inode->u.adfs_i.file_id), - off (inode->u.adfs_i.file_id) + block); + blk = adfs_map_lookup (sb, frag_id(inode->u.adfs_i.file_id), block); return blk; } @@ -105,13 +114,13 @@ unsigned int adfs_parent_bmap (struct inode *inode, int block) fragment = inode_frag (inode->i_ino); if (frag_id (fragment) == ADFS_ROOT_FRAG) - blk = sb->u.adfs_sb.s_map_block + off (fragment) + block; + blk = sb->u.adfs_sb.s_map_block + off(fragment) + block; else - blk = adfs_map_lookup (sb, frag_id (fragment), off (fragment) + block); + blk = adfs_map_lookup (sb, frag_id (fragment), off(fragment) + block); return blk; } -static int adfs_atts2mode (unsigned char mode, unsigned int filetype) +static int adfs_atts2mode(struct super_block *sb, unsigned char mode, unsigned int filetype) { int omode = 0; @@ -120,24 +129,29 @@ static int adfs_atts2mode (unsigned char mode, unsigned int filetype) S_IRGRP|S_IWGRP|S_IXGRP| S_IROTH|S_IWOTH|S_IXOTH; } else { - if (mode & ADFS_NDA_DIRECTORY) - omode |= S_IFDIR|S_IRUSR|S_IXUSR|S_IXGRP|S_IXOTH; - else + if (mode & ADFS_NDA_DIRECTORY) { + omode |= S_IRUGO & sb->u.adfs_sb.s_owner_mask; + omode |= S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH; + } else omode |= S_IFREG; + if (mode & ADFS_NDA_OWNER_READ) { - omode |= S_IRUSR; + omode |= S_IRUGO & sb->u.adfs_sb.s_owner_mask; if (filetype == 0xfe6 /* UnixExec */) - omode |= S_IXUSR; + omode |= S_IXUGO & sb->u.adfs_sb.s_owner_mask; } + if (mode & ADFS_NDA_OWNER_WRITE) - omode |= S_IWUSR; + omode |= S_IWUGO & sb->u.adfs_sb.s_owner_mask; + if (mode & ADFS_NDA_PUBLIC_READ) { - omode |= S_IRGRP | S_IROTH; - if (filetype == 0xfe6) - omode |= S_IXGRP | S_IXOTH; + omode |= S_IRUGO & sb->u.adfs_sb.s_other_mask; + if (filetype == 0xfe6 /* UnixExec */) + omode |= S_IXUGO & sb->u.adfs_sb.s_other_mask; } + if (mode & ADFS_NDA_PUBLIC_WRITE) - omode |= S_IWGRP | S_IWOTH; + omode |= S_IWUGO & sb->u.adfs_sb.s_other_mask; } return omode; } @@ -150,8 +164,8 @@ void adfs_read_inode (struct inode *inode) int buffers; sb = inode->i_sb; - inode->i_uid = 0; - inode->i_gid = 0; + inode->i_uid = sb->u.adfs_sb.s_uid; + inode->i_gid = sb->u.adfs_sb.s_gid; inode->i_version = ++event; if (adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00)) { @@ -186,7 +200,7 @@ void adfs_read_inode (struct inode *inode) goto bad; } adfs_dir_free (bh, buffers); - inode->i_mode = adfs_atts2mode (ide.mode, ide.filetype); + inode->i_mode = adfs_atts2mode(sb, ide.mode, ide.filetype); inode->i_nlink = 2; inode->i_size = ide.size; inode->i_blksize = PAGE_SIZE; @@ -204,13 +218,5 @@ void adfs_read_inode (struct inode *inode) return; bad: - inode->i_mode = 0; - inode->i_nlink = 1; - inode->i_size = 0; - inode->i_blksize = 0; - inode->i_blocks = 0; - inode->i_mtime = - inode->i_atime = - inode->i_ctime = 0; - inode->i_op = NULL; + make_bad_inode(inode); } diff --git a/fs/adfs/namei.c b/fs/adfs/namei.c index 24f0565d9..df3b5e457 100644 --- a/fs/adfs/namei.c +++ b/fs/adfs/namei.c @@ -46,7 +46,7 @@ static int adfs_find_entry (struct inode *dir, const char * const name, int name unsigned long parent_object_id, dir_object_id; int buffers, pos; - if (!dir || !S_ISDIR(dir->i_mode)) + if (!S_ISDIR(dir->i_mode)) return 0; sb = dir->i_sb; @@ -98,22 +98,22 @@ static int adfs_find_entry (struct inode *dir, const char * const name, int name return 0; } -int adfs_lookup (struct inode *dir, struct dentry *dentry) +struct dentry *adfs_lookup (struct inode *dir, struct dentry *dentry) { struct inode *inode = NULL; struct adfs_idir_entry de; unsigned long ino; if (dentry->d_name.len > ADFS_NAME_LEN) - return -ENAMETOOLONG; + return ERR_PTR(-ENAMETOOLONG); if (adfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de)) { ino = de.inode_no; inode = iget (dir->i_sb, ino); if (!inode) - return -EACCES; + return ERR_PTR(-EACCES); } d_add(dentry, inode); - return 0; + return NULL; } diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 234d8cf21..8c2fbe8fa 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -21,25 +21,26 @@ #include <stdarg.h> -static void adfs_put_super (struct super_block *sb); -static int adfs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz); -void adfs_read_inode (struct inode *inode); +static void adfs_put_super(struct super_block *sb); +static int adfs_remount(struct super_block *sb, int *flags, char *data); +static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); +void adfs_read_inode(struct inode *inode); -void adfs_error (struct super_block *sb, const char *function, const char *fmt, ...) +void adfs_error(struct super_block *sb, const char *function, const char *fmt, ...) { char error_buf[128]; va_list args; - va_start (args, fmt); - vsprintf (error_buf, fmt, args); - va_end (args); + va_start(args, fmt); + vsprintf(error_buf, fmt, args); + va_end(args); - printk (KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n", - kdevname (sb->s_dev), function ? ": " : "", + printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n", + kdevname(sb->s_dev), function ? ": " : "", function ? function : "", error_buf); } -unsigned char adfs_calccrosscheck (struct super_block *sb, char *map) +static unsigned char adfs_calczonecheck(struct super_block *sb, char *map) { unsigned int v0, v1, v2, v3; int i; @@ -63,7 +64,7 @@ unsigned char adfs_calccrosscheck (struct super_block *sb, char *map) return v0 ^ v1 ^ v2 ^ v3; } -static int adfs_checkmap (struct super_block *sb) +static int adfs_checkmap(struct super_block *sb) { unsigned char crosscheck = 0, zonecheck = 1; int i; @@ -72,14 +73,14 @@ static int adfs_checkmap (struct super_block *sb) char *map; map = sb->u.adfs_sb.s_map[i]->b_data; - if (adfs_calccrosscheck (sb, map) != map[0]) { - adfs_error (sb, "adfs_checkmap", "zone %d fails zonecheck", i); + if (adfs_calczonecheck(sb, map) != map[0]) { + adfs_error(sb, "adfs_checkmap", "zone %d fails zonecheck", i); zonecheck = 0; } crosscheck ^= map[3]; } if (crosscheck != 0xff) - adfs_error (sb, "adfs_checkmap", "crosscheck != 0xff"); + adfs_error(sb, "adfs_checkmap", "crosscheck != 0xff"); return crosscheck == 0xff && zonecheck; } @@ -92,21 +93,73 @@ static struct super_operations adfs_sops = { adfs_put_super, NULL, adfs_statfs, - NULL + adfs_remount }; -static void adfs_put_super (struct super_block *sb) +static void adfs_put_super(struct super_block *sb) { int i; for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) - brelse (sb->u.adfs_sb.s_map[i]); - kfree (sb->u.adfs_sb.s_map); - brelse (sb->u.adfs_sb.s_sbh); + brelse(sb->u.adfs_sb.s_map[i]); + kfree(sb->u.adfs_sb.s_map); + brelse(sb->u.adfs_sb.s_sbh); MOD_DEC_USE_COUNT; } -struct super_block *adfs_read_super (struct super_block *sb, void *data, int silent) +static int parse_options(struct super_block *sb, char *options) +{ + char *value, *opt; + + if (!options) + return 0; + + for (opt = strtok(options, ","); opt != NULL; opt = strtok(NULL, ",")) { + value = strchr(opt, '='); + if (value) + *value++ = '\0'; + + if (!strcmp(opt, "uid")) { /* owner of all files */ + if (!value || !*value) + return -EINVAL; + sb->u.adfs_sb.s_uid = simple_strtoul(value, &value, 0); + if (*value) + return -EINVAL; + } else + if (!strcmp(opt, "gid")) { /* group owner of all files */ + if (!value || !*value) + return -EINVAL; + sb->u.adfs_sb.s_gid = simple_strtoul(value, &value, 0); + if (*value) + return -EINVAL; + } else + if (!strcmp(opt, "ownmask")) { /* owner permission mask */ + if (!value || !*value) + return -EINVAL; + sb->u.adfs_sb.s_owner_mask = simple_strtoul(value, &value, 8); + if (*value) + return -EINVAL; + } else + if (!strcmp(opt, "othmask")) { /* others permission mask */ + if (!value || !*value) + return -EINVAL; + sb->u.adfs_sb.s_other_mask = simple_strtoul(value, &value, 8); + if (*value) + return -EINVAL; + } else { /* eh? say again. */ + printk("ADFS-fs: unrecognised mount option %s\n", opt); + return -EINVAL; + } + } + return 0; +} + +static int adfs_remount(struct super_block *sb, int *flags, char *data) +{ + return parse_options(sb, data); +} + +struct super_block *adfs_read_super(struct super_block *sb, void *data, int silent) { struct adfs_discrecord *dr; struct buffer_head *bh; @@ -114,28 +167,30 @@ struct super_block *adfs_read_super (struct super_block *sb, void *data, int sil kdev_t dev = sb->s_dev; int i, j; + /* set default options */ + sb->u.adfs_sb.s_uid = 0; + sb->u.adfs_sb.s_gid = 0; + sb->u.adfs_sb.s_owner_mask = S_IRWXU; + sb->u.adfs_sb.s_other_mask = S_IRWXG | S_IRWXO; + + if (parse_options(sb, data)) + goto error; + MOD_INC_USE_COUNT; - lock_super (sb); - set_blocksize (dev, BLOCK_SIZE); - if (!(bh = bread (dev, ADFS_DISCRECORD / BLOCK_SIZE, BLOCK_SIZE))) { - unlock_super (sb); - adfs_error (sb, NULL, "unable to read superblock"); - MOD_DEC_USE_COUNT; - return NULL; + lock_super(sb); + set_blocksize(dev, BLOCK_SIZE); + if (!(bh = bread(dev, ADFS_DISCRECORD / BLOCK_SIZE, BLOCK_SIZE))) { + adfs_error(sb, NULL, "unable to read superblock"); + goto error_unlock; } b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE); - if (adfs_checkbblk (b_data)) { + if (adfs_checkbblk(b_data)) { if (!silent) - printk ("VFS: Can't find an adfs filesystem on dev " + printk("VFS: Can't find an adfs filesystem on dev " "%s.\n", kdevname(dev)); -failed_mount: - unlock_super (sb); - if (bh) - brelse (bh); - MOD_DEC_USE_COUNT; - return NULL; + goto error_free_bh; } dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); @@ -145,26 +200,26 @@ failed_mount: (sb->s_blocksize == 512 || sb->s_blocksize == 1024 || sb->s_blocksize == 2048 || sb->s_blocksize == 4096)) { - brelse (bh); - set_blocksize (dev, sb->s_blocksize); - bh = bread (dev, ADFS_DISCRECORD / sb->s_blocksize, sb->s_blocksize); + brelse(bh); + set_blocksize(dev, sb->s_blocksize); + bh = bread(dev, ADFS_DISCRECORD / sb->s_blocksize, sb->s_blocksize); if (!bh) { - adfs_error (sb, NULL, "couldn't read superblock on " + adfs_error(sb, NULL, "couldn't read superblock on " "2nd try."); - goto failed_mount; + goto error_unlock; } b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize); - if (adfs_checkbblk (b_data)) { - adfs_error (sb, NULL, "disc record mismatch, very weird!"); - goto failed_mount; + if (adfs_checkbblk(b_data)) { + adfs_error(sb, NULL, "disc record mismatch, very weird!"); + goto error_free_bh; } dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); } if (sb->s_blocksize != bh->b_size) { if (!silent) - printk (KERN_ERR "VFS: Unsupported blocksize on dev " - "%s.\n", kdevname (dev)); - goto failed_mount; + printk(KERN_ERR "VFS: Unsupported blocksize on dev " + "%s.\n", kdevname(dev)); + goto error_free_bh; } /* blocksize on this device should now be set to the adfs log2secsize */ @@ -202,71 +257,81 @@ failed_mount: else sb->u.adfs_sb.s_map_block >>= -sb->u.adfs_sb.s_map2blk; - printk (KERN_DEBUG "ADFS: zone size %d, IDs per zone %d, map address %X size %d sectors\n", + printk(KERN_DEBUG "ADFS: zone size %d, IDs per zone %d, map address %X size %d sectors\n", sb->u.adfs_sb.s_zone_size, sb->u.adfs_sb.s_ids_per_zone, sb->u.adfs_sb.s_map_block, sb->u.adfs_sb.s_map_size); - printk (KERN_DEBUG "ADFS: sector size %d, map bit size %d\n", - 1 << dr->log2secsize, 1 << dr->log2bpmb); + printk(KERN_DEBUG "ADFS: sector size %d, map bit size %d, share size %d\n", + 1 << dr->log2secsize, 1 << dr->log2bpmb, + 1 << (dr->log2secsize + dr->log2sharesize)); sb->s_magic = ADFS_SUPER_MAGIC; - sb->s_flags |= MS_RDONLY; /* we don't support writing yet */ - sb->u.adfs_sb.s_map = kmalloc (sb->u.adfs_sb.s_map_size * - sizeof (struct buffer_head *), GFP_KERNEL); + sb->u.adfs_sb.s_map = kmalloc(sb->u.adfs_sb.s_map_size * + sizeof(struct buffer_head *), GFP_KERNEL); if (sb->u.adfs_sb.s_map == NULL) { - adfs_error (sb, NULL, "not enough memory"); - goto failed_mount; + adfs_error(sb, NULL, "not enough memory"); + goto error_free_bh; } for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) { - sb->u.adfs_sb.s_map[i] = bread (dev, + sb->u.adfs_sb.s_map[i] = bread(dev, sb->u.adfs_sb.s_map_block + i, sb->s_blocksize); if (!sb->u.adfs_sb.s_map[i]) { for (j = 0; j < i; j++) - brelse (sb->u.adfs_sb.s_map[j]); - kfree (sb->u.adfs_sb.s_map); - adfs_error (sb, NULL, "unable to read map"); - goto failed_mount; + brelse(sb->u.adfs_sb.s_map[j]); + kfree(sb->u.adfs_sb.s_map); + adfs_error(sb, NULL, "unable to read map"); + goto error_free_bh; } } - if (!adfs_checkmap (sb)) { + if (!adfs_checkmap(sb)) { for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) - brelse (sb->u.adfs_sb.s_map[i]); - adfs_error (sb, NULL, "map corrupted"); - goto failed_mount; + brelse(sb->u.adfs_sb.s_map[i]); + adfs_error(sb, NULL, "map corrupted"); + goto error_free_bh; } dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0]->b_data + 4); - unlock_super (sb); + unlock_super(sb); /* * set up enough so that it can read an inode */ sb->s_op = &adfs_sops; - sb->u.adfs_sb.s_root = adfs_inode_generate (dr->root, 0); + sb->u.adfs_sb.s_root = adfs_inode_generate(dr->root, 0); sb->s_root = d_alloc_root(iget(sb, sb->u.adfs_sb.s_root), NULL); if (!sb->s_root) { - sb->s_dev = 0; for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) - brelse (sb->u.adfs_sb.s_map[i]); - brelse (bh); - adfs_error (sb, NULL, "get root inode failed\n"); - MOD_DEC_USE_COUNT; - return NULL; + brelse(sb->u.adfs_sb.s_map[i]); + brelse(bh); + adfs_error(sb, NULL, "get root inode failed\n"); + goto error_dec_use; } return sb; + +error_free_bh: + if (bh) + brelse(bh); +error_unlock: + unlock_super(sb); +error_dec_use: + MOD_DEC_USE_COUNT; +error: + sb->s_dev = 0; + return NULL; } -static int adfs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) +static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; const unsigned int nidlen = sb->u.adfs_sb.s_idlen + 1; tmp.f_type = ADFS_SUPER_MAGIC; tmp.f_bsize = sb->s_blocksize; - tmp.f_blocks = (sb->u.adfs_sb.s_dr->disc_size) >> (sb->s_blocksize_bits); + tmp.f_blocks = sb->u.adfs_sb.s_dr->disc_size_high << (32 - sb->s_blocksize_bits) | + sb->u.adfs_sb.s_dr->disc_size >> sb->s_blocksize_bits; tmp.f_files = tmp.f_blocks >> nidlen; { unsigned int i, j = 0; @@ -305,35 +370,35 @@ static int adfs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) if (freelink <= nidlen) break; } while (mapindex < 8 * sb->s_blocksize); if (mapindex > 8 * sb->s_blocksize) - adfs_error (sb, NULL, "oversized free fragment\n"); + adfs_error(sb, NULL, "oversized free fragment\n"); else if (freelink) - adfs_error (sb, NULL, "undersized free fragment\n"); + adfs_error(sb, NULL, "undersized free fragment\n"); } tmp.f_bfree = tmp.f_bavail = j << (sb->u.adfs_sb.s_dr->log2bpmb - sb->s_blocksize_bits); } tmp.f_ffree = tmp.f_bfree >> nidlen; tmp.f_namelen = ADFS_NAME_LEN; - return copy_to_user (buf, &tmp, bufsiz) ? -EFAULT : 0; + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } static struct file_system_type adfs_fs_type = { "adfs", FS_REQUIRES_DEV, adfs_read_super, NULL }; -__initfunc(int init_adfs_fs (void)) +__initfunc(int init_adfs_fs(void)) { - return register_filesystem (&adfs_fs_type); + return register_filesystem(&adfs_fs_type); } #ifdef MODULE -int init_module (void) +int init_module(void) { return init_adfs_fs(); } -void cleanup_module (void) +void cleanup_module(void) { - unregister_filesystem (&adfs_fs_type); + unregister_filesystem(&adfs_fs_type); } #endif diff --git a/fs/affs/Changes b/fs/affs/Changes index 0432bdd0d..7a7faedd0 100644 --- a/fs/affs/Changes +++ b/fs/affs/Changes @@ -28,6 +28,15 @@ Known bugs: Please direct bug reports to: hjw@zvw.de +Version 3.10 +------------ + +- Changed partition checker to allow devices + with physical blocks != 512 bytes. + +- The partition checker now also ignores the + word at 0xd0 that Windows likes to write to. + Version 3.9 ----------- diff --git a/fs/affs/dir.c b/fs/affs/dir.c index 51e45b682..3a1c78ef0 100644 --- a/fs/affs/dir.c +++ b/fs/affs/dir.c @@ -90,9 +90,6 @@ affs_readdir(struct file *filp, void *dirent, filldir_t filldir) pr_debug("AFFS: readdir(ino=%lu,f_pos=%lu)\n",inode->i_ino,(unsigned long)filp->f_pos); - if (!inode || !S_ISDIR(inode->i_mode)) - return -EBADF; - stored = 0; dir_bh = NULL; fh_bh = NULL; diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 80aad129c..9b05ec062 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -21,7 +21,6 @@ #include <linux/mm.h> #include <linux/string.h> #include <linux/locks.h> -#include <linux/errno.h> #include <linux/genhd.h> #include <linux/amigaffs.h> #include <linux/major.h> @@ -293,7 +292,7 @@ affs_new_inode(const struct inode *dir) sb = dir->i_sb; inode->i_sb = sb; - inode->i_flags = sb->s_flags; + inode->i_flags = 0; if (!(block = affs_new_header((struct inode *)dir))) { iput(inode); diff --git a/fs/affs/namei.c b/fs/affs/namei.c index a18c23a0f..48e951800 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -201,7 +201,7 @@ affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino) return bh; } -int +struct dentry * affs_lookup(struct inode *dir, struct dentry *dentry) { unsigned long ino; @@ -218,11 +218,11 @@ affs_lookup(struct inode *dir, struct dentry *dentry) affs_brelse(bh); inode = iget(dir->i_sb,ino); if (!inode) - return -EACCES; + return ERR_PTR(-EACCES); } dentry->d_op = &affs_dentry_operations; d_add(dentry,inode); - return 0; + return NULL; } int @@ -548,32 +548,14 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry, "No inode for entry found (key=%lu)\n",new_ino); goto end_rename; } - if (new_inode == old_inode) { - if (old_ino == new_ino) { /* Filename might have changed case */ - retval = new_dentry->d_name.len < 31 ? new_dentry->d_name.len : 30; - strncpy(DIR_END(old_bh->b_data,old_inode)->dir_name + 1, - new_dentry->d_name.name,retval); - DIR_END(old_bh->b_data,old_inode)->dir_name[0] = retval; - goto new_checksum; - } - retval = 0; - goto end_rename; - } if (S_ISDIR(old_inode->i_mode)) { - retval = -EINVAL; - if (is_subdir(new_dentry, old_dentry)) - goto end_rename; if (new_inode) { - if (new_dentry->d_count > 1) - shrink_dcache_parent(new_dentry); - retval = -EBUSY; - if (new_dentry->d_count > 1) - goto end_rename; retval = -ENOTEMPTY; if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode))) goto end_rename; } + retval = -ENOENT; if (affs_parent_ino(old_inode) != old_dir->i_ino) goto end_rename; } @@ -593,7 +575,6 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry, affs_copy_name(FILE_END(old_bh->b_data,old_inode)->file_name,new_dentry->d_name.name); if ((retval = affs_insert_hash(new_dir->i_ino,old_bh,new_dir))) goto end_rename; -new_checksum: affs_fix_checksum(AFFS_I2BSIZE(new_dir),old_bh->b_data,5); new_dir->i_ctime = new_dir->i_mtime = old_dir->i_ctime @@ -604,7 +585,6 @@ new_checksum: mark_inode_dirty(new_dir); mark_inode_dirty(old_dir); mark_buffer_dirty(old_bh,1); - d_move(old_dentry,new_dentry); end_rename: affs_brelse(old_bh); diff --git a/fs/affs/super.c b/fs/affs/super.c index 464b6df8a..0c2a838f5 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -22,7 +22,6 @@ #include <linux/mm.h> #include <linux/string.h> #include <linux/locks.h> -#include <linux/errno.h> #include <linux/genhd.h> #include <linux/amigaffs.h> #include <linux/major.h> diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index 63efe9b87..d6944e889 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -38,10 +38,10 @@ static int autofs_dir_readdir(struct file *filp, /* * 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) +static struct dentry *autofs_dir_lookup(struct inode *dir,struct dentry *dentry) { d_add(dentry, NULL); - return 0; + return NULL; } static struct file_operations autofs_dir_operations = { diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 561904318..c0caee9df 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -16,7 +16,7 @@ #include "autofs_i.h" static int autofs_root_readdir(struct file *,void *,filldir_t); -static int autofs_root_lookup(struct inode *,struct dentry *); +static struct dentry *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 *); @@ -168,7 +168,7 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str * yet completely filled in, and revalidate has to delay such * lookups.. */ -static int autofs_revalidate(struct dentry * dentry) +static int autofs_revalidate(struct dentry * dentry, int flags) { struct inode * dir = dentry->d_parent->d_inode; struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); @@ -209,7 +209,7 @@ static struct dentry_operations autofs_dentry_operations = { NULL, /* d_compare */ }; -static int autofs_root_lookup(struct inode *dir, struct dentry * dentry) +static struct dentry *autofs_root_lookup(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi; int oz_mode; @@ -217,8 +217,8 @@ static int autofs_root_lookup(struct inode *dir, struct dentry * dentry) DPRINTK(("autofs_root_lookup: name = ")); autofs_say(dentry->d_name.name,dentry->d_name.len); - if (!S_ISDIR(dir->i_mode)) - return -ENOTDIR; + if (dentry->d_name.len > NAME_MAX) + return ERR_PTR(-ENOENT);/* File name too long to exist */ sbi = autofs_sbi(dir->i_sb); @@ -241,7 +241,7 @@ static int autofs_root_lookup(struct inode *dir, struct dentry * dentry) d_add(dentry, NULL); up(&dir->i_sem); - autofs_revalidate(dentry); + autofs_revalidate(dentry, 0); down(&dir->i_sem); /* @@ -250,7 +250,7 @@ static int autofs_root_lookup(struct inode *dir, struct dentry * dentry) */ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) { if (signal_pending(current)) - return -ERESTARTNOINTR; + return ERR_PTR(-ERESTARTNOINTR); } /* @@ -260,9 +260,9 @@ static int autofs_root_lookup(struct inode *dir, struct dentry * dentry) * be OK for the operations we permit from an autofs. */ if ( dentry->d_inode && list_empty(&dentry->d_hash) ) - return -ENOENT; + return ERR_PTR(-ENOENT); - return 0; + return NULL; } static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const char *symname) @@ -278,7 +278,10 @@ static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const c autofs_say(dentry->d_name.name,dentry->d_name.len); if ( !autofs_oz_mode(sbi) ) - return -EPERM; + return -EACCES; + + if ( dentry->d_name.len > NAME_MAX ) + return -ENAMETOOLONG; if ( autofs_hash_lookup(dh, &dentry->d_name) ) return -EEXIST; @@ -343,17 +346,20 @@ static int autofs_root_unlink(struct inode *dir, struct dentry *dentry) struct autofs_dir_ent *ent; unsigned int n; - if ( !autofs_oz_mode(sbi) ) - return -EPERM; + /* This allows root to remove symlinks */ + if ( !autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) + return -EACCES; ent = autofs_hash_lookup(dh, &dentry->d_name); if ( !ent ) return -ENOENT; n = ent->ino - AUTOFS_FIRST_SYMLINK; - if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap) ) - return -EINVAL; /* Not a symlink inode, can't unlink */ - + if ( n >= AUTOFS_MAX_SYMLINKS ) + return -EISDIR; /* It's a directory, dummy */ + if ( !test_bit(n,sbi->symlink_bitmap) ) + return -EINVAL; /* Nonexistent symlink? Shouldn't happen */ + dentry->d_time = (unsigned long)(struct autofs_dirhash *)NULL; autofs_hash_delete(ent); clear_bit(n,sbi->symlink_bitmap); @@ -370,7 +376,7 @@ static int autofs_root_rmdir(struct inode *dir, struct dentry *dentry) struct autofs_dir_ent *ent; if ( !autofs_oz_mode(sbi) ) - return -EPERM; + return -EACCES; ent = autofs_hash_lookup(dh, &dentry->d_name); if ( !ent ) @@ -399,7 +405,10 @@ static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode) ino_t ino; if ( !autofs_oz_mode(sbi) ) - return -EPERM; + return -EACCES; + + if ( dentry->d_name.len > NAME_MAX ) + return -ENAMETOOLONG; ent = autofs_hash_lookup(dh, &dentry->d_name); if ( ent ) diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c index 81c68af46..f9cccbd44 100644 --- a/fs/autofs/waitq.c +++ b/fs/autofs/waitq.c @@ -99,7 +99,7 @@ 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, struct qstr * name) +int autofs_wait(struct autofs_sb_info *sbi, struct qstr *name) { struct autofs_wait_queue *wq; int status; @@ -107,6 +107,10 @@ int autofs_wait(struct autofs_sb_info *sbi, struct qstr * name) /* In catatonic mode, we don't wait for nobody */ if ( sbi->catatonic ) return -ENOENT; + + /* We shouldn't be able to get here, but just in case */ + if ( name->len > NAME_MAX ) + return -ENOENT; for ( wq = sbi->queues ; wq ; wq = wq->next ) { if ( wq->hash == name->hash && diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index c7925b235..a4ed4eb34 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -58,14 +58,25 @@ static void set_brk(unsigned long start, unsigned long end) * These are the only things you should do on a core-file: use only these * macros to write out all the necessary info. */ -#define DUMP_WRITE(addr,nr) \ -while (file.f_op->write(&file,(char *)(addr),(nr),&file.f_pos) != (nr)) goto close_coredump + +static int dump_write(struct file *file, const void *addr, int nr) +{ + int r; + down(&file->f_dentry->d_inode->i_sem); + r = file->f_op->write(file, addr, nr, &file->f_pos) == nr; + up(&file->f_dentry->d_inode->i_sem); + return r; +} + +#define DUMP_WRITE(addr, nr) \ + if (!dump_write(file, (void *)(addr), (nr))) \ + goto close_coredump; #define DUMP_SEEK(offset) \ -if (file.f_op->llseek) { \ - if (file.f_op->llseek(&file,(offset),0) != (offset)) \ +if (file->f_op->llseek) { \ + if (file->f_op->llseek(file,(offset),0) != (offset)) \ goto close_coredump; \ -} else file.f_pos = (offset) +} else file->f_pos = (offset) /* * Routine writes a core dump image in the current directory. @@ -82,7 +93,7 @@ do_aout_core_dump(long signr, struct pt_regs * regs) { struct dentry * dentry = NULL; struct inode * inode = NULL; - struct file file; + struct file * file; mm_segment_t fs; int has_dumped = 0; char corefile[6+sizeof(current->comm)]; @@ -116,21 +127,16 @@ do_aout_core_dump(long signr, struct pt_regs * regs) #else corefile[4] = '\0'; #endif - dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); - if (IS_ERR(dentry)) { - dentry = NULL; + file = filp_open(corefile,O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); + if (IS_ERR(file)) goto end_coredump; - } + dentry = file->f_dentry; inode = dentry->d_inode; if (!S_ISREG(inode->i_mode)) - goto end_coredump; + goto close_coredump; if (!inode->i_op || !inode->i_op->default_file_ops) - goto end_coredump; - if (get_write_access(inode)) - goto end_coredump; - if (init_private_file(&file, dentry, 3)) - goto end_coredump_write; - if (!file.f_op->write) + goto close_coredump; + if (!file->f_op->write) goto close_coredump; has_dumped = 1; current->flags |= PF_DUMPCORE; @@ -211,13 +217,9 @@ do_aout_core_dump(long signr, struct pt_regs * regs) set_fs(KERNEL_DS); DUMP_WRITE(current,sizeof(*current)); close_coredump: - if (file.f_op->release) - file.f_op->release(inode,&file); -end_coredump_write: - put_write_access(inode); + filp_close(file, NULL); end_coredump: set_fs(fs); - dput(dentry); return has_dumped; } @@ -327,6 +329,8 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs } if (N_MAGIC(ex) == ZMAGIC && ex.a_text && + bprm->dentry->d_inode->i_op && + bprm->dentry->d_inode->i_op->bmap && (fd_offset < bprm->dentry->d_inode->i_sb->s_blocksize)) { printk(KERN_NOTICE "N_TXTOFF < BLOCK_SIZE. Please convert binary.\n"); return -ENOEXEC; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 8be2fc475..82f75d1e6 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -927,7 +927,11 @@ static int load_elf_library(int fd) */ static int dump_write(struct file *file, const void *addr, int nr) { - return file->f_op->write(file, addr, nr, &file->f_pos) == nr; + int r; + down(&file->f_dentry->d_inode->i_sem); + r = file->f_op->write(file, addr, nr, &file->f_pos) == nr; + up(&file->f_dentry->d_inode->i_sem); + return r; } static int dump_seek(struct file *file, off_t off) @@ -1032,10 +1036,10 @@ static int writenote(struct memelfnote *men, struct file *file) #undef DUMP_SEEK #define DUMP_WRITE(addr, nr) \ - if (!dump_write(&file, (addr), (nr))) \ + if (!dump_write(file, (addr), (nr))) \ goto close_coredump; #define DUMP_SEEK(off) \ - if (!dump_seek(&file, (off))) \ + if (!dump_seek(file, (off))) \ goto close_coredump; /* * Actual dumper @@ -1047,7 +1051,7 @@ static int writenote(struct memelfnote *men, struct file *file) static int elf_core_dump(long signr, struct pt_regs * regs) { int has_dumped = 0; - struct file file; + struct file *file; struct dentry *dentry; struct inode *inode; mm_segment_t fs; @@ -1118,30 +1122,28 @@ static int elf_core_dump(long signr, struct pt_regs * regs) fs = get_fs(); set_fs(KERNEL_DS); + memcpy(corefile,"core.",5); #if 0 memcpy(corefile+5,current->comm,sizeof(current->comm)); #else corefile[4] = '\0'; #endif - dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); - if (IS_ERR(dentry)) { - dentry = NULL; + file = filp_open(corefile, O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); + if (IS_ERR(file)) goto end_coredump; - } + dentry = file->f_dentry; inode = dentry->d_inode; - - if(inode->i_nlink > 1) - goto end_coredump; /* multiple links - don't dump */ + if (inode->i_nlink > 1) + goto close_coredump; /* multiple links - don't dump */ if (!S_ISREG(inode->i_mode)) - goto end_coredump; + goto close_coredump; if (!inode->i_op || !inode->i_op->default_file_ops) - goto end_coredump; - if (init_private_file(&file, dentry, 3)) - goto end_coredump; - if (!file.f_op->write) goto close_coredump; + if (!file->f_op->write) + goto close_coredump; + has_dumped = 1; current->flags |= PF_DUMPCORE; @@ -1297,7 +1299,7 @@ static int elf_core_dump(long signr, struct pt_regs * regs) } for(i = 0; i < numnote; i++) - if (!writenote(¬es[i], &file)) + if (!writenote(¬es[i], file)) goto close_coredump; set_fs(fs); @@ -1319,19 +1321,17 @@ static int elf_core_dump(long signr, struct pt_regs * regs) DUMP_WRITE((void *)addr, len); } - if ((off_t) file.f_pos != offset) { + if ((off_t) file->f_pos != offset) { /* Sanity check */ - printk("elf_core_dump: file.f_pos (%ld) != offset (%ld)\n", - (off_t) file.f_pos, offset); + printk("elf_core_dump: file->f_pos (%ld) != offset (%ld)\n", + (off_t) file->f_pos, offset); } close_coredump: - if (file.f_op->release) - file.f_op->release(inode,&file); + filp_close(file, NULL); end_coredump: set_fs(fs); - dput(dentry); #ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; #endif diff --git a/fs/buffer.c b/fs/buffer.c index 2c874f742..afec12e55 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -76,6 +76,7 @@ static int nr_buffers = 0; static int nr_buffers_type[NR_LIST] = {0,}; static int nr_buffer_heads = 0; static int nr_unused_buffer_heads = 0; +static int nr_hashed_buffers = 0; /* This is used by some architectures to estimate available memory. */ int buffermem = 0; @@ -99,7 +100,8 @@ union bdflush_param{ each time we call refill */ int nref_dirt; /* Dirty buffer threshold for activating bdflush when trying to refill buffers. */ - int dummy1; /* unused */ + int interval; /* Interval (seconds) between spontaneous + bdflush runs */ int age_buffer; /* Time for normal buffer to age before we flush it */ int age_super; /* Time for superblock to age before we @@ -108,10 +110,10 @@ union bdflush_param{ int dummy3; /* unused */ } b_un; unsigned int data[N_PARAM]; -} bdf_prm = {{40, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}}; +} bdf_prm = {{40, 500, 64, 256, 5, 30*HZ, 5*HZ, 1884, 2}}; /* These are the min and max parameter values that we will allow to be assigned */ -int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 1*HZ, 1*HZ, 1, 1}; +int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 1, 1*HZ, 1*HZ, 1, 1}; int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 600*HZ, 600*HZ, 2047, 5}; void wakeup_bdflush(int); @@ -383,7 +385,9 @@ asmlinkage int sys_fdatasync(unsigned int fd) goto out_putf; /* this needs further work, at the moment it is identical to fsync() */ + down(&inode->i_sem); err = file->f_op->fsync(file, dentry); + up(&inode->i_sem); out_putf: fput(file); @@ -432,6 +436,7 @@ static inline void remove_from_hash_queue(struct buffer_head * bh) *pprev = next; bh->b_pprev = NULL; } + nr_hashed_buffers--; } static inline void remove_from_lru_list(struct buffer_head * bh) @@ -482,33 +487,6 @@ static void remove_from_queues(struct buffer_head * bh) remove_from_lru_list(bh); } -static inline void put_last_lru(struct buffer_head * bh) -{ - if (bh) { - struct buffer_head **bhp = &lru_list[bh->b_list]; - - if (bh == *bhp) { - *bhp = bh->b_next_free; - return; - } - - if(bh->b_dev == B_FREE) - panic("Wrong block for lru list"); - - /* Add to back of free list. */ - remove_from_lru_list(bh); - if(!*bhp) { - *bhp = bh; - (*bhp)->b_prev_free = bh; - } - - bh->b_next_free = *bhp; - bh->b_prev_free = (*bhp)->b_prev_free; - (*bhp)->b_prev_free->b_next_free = bh; - (*bhp)->b_prev_free = bh; - } -} - static inline void put_last_free(struct buffer_head * bh) { if (bh) { @@ -566,6 +544,7 @@ static void insert_into_queues(struct buffer_head * bh) *bhp = bh; bh->b_pprev = bhp; } + nr_hashed_buffers++; } } @@ -721,8 +700,6 @@ repeat: bh = get_hash_table(dev, block, size); if (bh) { if (!buffer_dirty(bh)) { - if (buffer_uptodate(bh)) - put_last_lru(bh); bh->b_flushtime = 0; } return bh; @@ -807,7 +784,7 @@ void refile_buffer(struct buffer_head * buf) * If too high a percentage of the buffers are dirty... */ if (nr_buffers_type[BUF_DIRTY] > too_many) - wakeup_bdflush(0); + wakeup_bdflush(1); /* If this is a loop device, and * more than half of the buffers are dirty... @@ -828,6 +805,7 @@ void __brelse(struct buffer_head * buf) /* If dirty, mark the time this buffer should be written back. */ set_writetime(buf, 0); refile_buffer(buf); + touch_buffer(buf); if (buf->b_count) { buf->b_count--; @@ -837,21 +815,21 @@ void __brelse(struct buffer_head * buf) } /* - * bforget() is like brelse(), except it removes the buffer - * from the hash-queues (so that it won't be re-used if it's - * shared). + * bforget() is like brelse(), except it puts the buffer on the + * free list if it can.. We can NOT free the buffer if: + * - there are other users of it + * - it is locked and thus can have active IO */ void __bforget(struct buffer_head * buf) { - mark_buffer_clean(buf); - clear_bit(BH_Protected, &buf->b_state); - remove_from_hash_queue(buf); - buf->b_dev = NODEV; - refile_buffer(buf); - if (!--buf->b_count) + if (buf->b_count != 1 || buffer_locked(buf)) { + __brelse(buf); return; - printk("VFS: forgot an in-use buffer! (count=%d)\n", - buf->b_count); + } + buf->b_count = 0; + buf->b_state = 0; + remove_from_queues(buf); + put_last_free(buf); } /* @@ -860,20 +838,16 @@ void __bforget(struct buffer_head * buf) */ struct buffer_head * bread(kdev_t dev, int block, int size) { - struct buffer_head * bh = getblk(dev, block, size); + struct buffer_head * bh; - if (bh) { - touch_buffer(bh); - if (buffer_uptodate(bh)) - return bh; - ll_rw_block(READ, 1, &bh); - wait_on_buffer(bh); - if (buffer_uptodate(bh)) - return bh; - brelse(bh); - return NULL; - } - printk("VFS: bread: impossible error\n"); + bh = getblk(dev, block, size); + if (buffer_uptodate(bh)) + return bh; + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + if (buffer_uptodate(bh)) + return bh; + brelse(bh); return NULL; } @@ -897,12 +871,12 @@ struct buffer_head * breada(kdev_t dev, int block, int bufsize, if (pos >= filesize) return NULL; - if (block < 0 || !(bh = getblk(dev,block,bufsize))) + if (block < 0) return NULL; + bh = getblk(dev, block, bufsize); index = BUFSIZE_INDEX(bh->b_size); - touch_buffer(bh); if (buffer_uptodate(bh)) return(bh); else ll_rw_block(READ, 1, &bh); @@ -1008,9 +982,9 @@ static struct buffer_head * get_unused_buffer_head(int async) /* This is critical. We can't swap out pages to get * more buffer heads, because the swap-out may need - * more buffer-heads itself. Thus SLAB_ATOMIC. + * more buffer-heads itself. Thus SLAB_BUFFER. */ - if((bh = kmem_cache_alloc(bh_cachep, SLAB_ATOMIC)) != NULL) { + if((bh = kmem_cache_alloc(bh_cachep, SLAB_BUFFER)) != NULL) { memset(bh, 0, sizeof(*bh)); nr_buffer_heads++; return bh; @@ -1489,6 +1463,7 @@ void show_buffers(void) printk("Buffer memory: %6dkB\n",buffermem>>10); printk("Buffer heads: %6d\n",nr_buffer_heads); printk("Buffer blocks: %6d\n",nr_buffers); + printk("Buffer hashed: %6d\n",nr_hashed_buffers); for(nlist = 0; nlist < NR_LIST; nlist++) { found = locked = dirty = used = lastused = protected = 0; @@ -1522,13 +1497,27 @@ void show_buffers(void) * Use gfp() for the hash table to decrease TLB misses, use * SLAB cache for buffer heads. */ -void __init buffer_init(void) +void __init buffer_init(unsigned long memory_size) { - int order = 5; /* Currently maximum order.. */ + int order; unsigned int nr_hash; - nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct buffer_head *); - hash_table = (struct buffer_head **) __get_free_pages(GFP_ATOMIC, order); + /* we need to guess at the right sort of size for a buffer cache. + the heuristic from working with large databases and getting + fsync times (ext2) manageable, is the following */ + + memory_size >>= 20; + for (order = 5; (1UL << order) < memory_size; order++); + + /* try to allocate something until we get it or we're asking + for something that is really too small */ + + do { + nr_hash = (1UL << order) * PAGE_SIZE / + sizeof(struct buffer_head *); + hash_table = (struct buffer_head **) + __get_free_pages(GFP_ATOMIC, order); + } while (hash_table == NULL && --order > 4); if (!hash_table) panic("Failed to allocate buffer hash table\n"); @@ -1565,7 +1554,6 @@ void __init buffer_init(void) * response to dirty buffers. Once this process is activated, we write back * a limited number of buffers to the disks and then go back to sleep again. */ -static struct wait_queue * bdflush_wait = NULL; static struct wait_queue * bdflush_done = NULL; struct task_struct *bdflush_tsk = 0; @@ -1573,7 +1561,7 @@ void wakeup_bdflush(int wait) { if (current == bdflush_tsk) return; - wake_up(&bdflush_wait); + wake_up_process(bdflush_tsk); if (wait) { run_task_queue(&tq_disk); sleep_on(&bdflush_done); @@ -1582,77 +1570,107 @@ void wakeup_bdflush(int wait) /* - * Here we attempt to write back old buffers. We also try to flush inodes - * and supers as well, since this function is essentially "update", and - * otherwise there would be no way of ensuring that these quantities ever - * get written back. Ideally, we would have a timestamp on the inodes - * and superblocks so that we could write back only the old ones as well - */ + * Here we attempt to write back old buffers. + * To prevent deadlocks for a loop device: + * 1) Do non-blocking writes to loop (avoids deadlock with running + * out of request blocks). + * 2) But do a blocking write if the only dirty buffers are loop buffers + * (otherwise we go into an infinite busy-loop). + * 3) Quit writing loop blocks if a freelist went low (avoids deadlock + * with running out of free buffers for loop's "real" device). +*/ -static int sync_old_buffers(void) +static inline void sync_old_buffers(void) { int i; - int ndirty, nwritten; - int nlist; - int ncount; - struct buffer_head * bh, *next; - - sync_supers(0); - sync_inodes(0); - - ncount = 0; + int ndirty = 0; + int wrta_cmd = WRITEA; #ifdef DEBUG - for(nlist = 0; nlist < NR_LIST; nlist++) -#else - for(nlist = BUF_DIRTY; nlist <= BUF_DIRTY; nlist++) + int ncount = 0, nwritten = 0; #endif - { - ndirty = 0; - nwritten = 0; - repeat: + struct buffer_head * bh, *next; - bh = lru_list[nlist]; - if(bh) - for (i = nr_buffers_type[nlist]; i-- > 0; bh = next) { - /* We may have stalled while waiting for I/O to complete. */ - if(bh->b_list != nlist) goto repeat; - next = bh->b_next_free; - if(!lru_list[nlist]) { - printk("Dirty list empty %d\n", i); - break; - } - - /* Clean buffer on dirty list? Refile it */ - if (nlist == BUF_DIRTY && !buffer_dirty(bh) && !buffer_locked(bh)) - { - refile_buffer(bh); - continue; - } - - if (buffer_locked(bh) || !buffer_dirty(bh)) - continue; - ndirty++; - if(time_before(jiffies, bh->b_flushtime)) - continue; - nwritten++; - next->b_count++; - bh->b_count++; - bh->b_flushtime = 0; #ifdef DEBUG - if(nlist != BUF_DIRTY) ncount++; + bh = lru_list[BUF_CLEAN]; + if(bh) + for(i = nr_buffers_type[BUF_CLEAN]; --i > 0; bh = next) { + next = bh->b_next_free; + + /* Dirty/locked buffer on clean list? Refile it */ + if (buffer_locked(bh) || buffer_dirty(bh)) { + ncount++; + refile_buffer(bh); + } + } #endif - ll_rw_block(WRITE, 1, &bh); - bh->b_count--; - next->b_count--; - } + + bh = lru_list[BUF_LOCKED]; + if(bh) + for(i = nr_buffers_type[BUF_LOCKED]; --i > 0; bh = next) { + next = bh->b_next_free; + + /* Unlocked buffer on locked list? Refile it */ + if (!buffer_locked(bh)) + refile_buffer(bh); + } + + restart: + bh = lru_list[BUF_DIRTY]; + if(bh) + for (i = nr_buffers_type[BUF_DIRTY]; + i-- > 0 && ndirty < bdf_prm.b_un.ndirty; + bh = next) { + /* We may have stalled while waiting for + I/O to complete. */ + if(bh->b_list != BUF_DIRTY) + goto restart; + next = bh->b_next_free; + if(!lru_list[BUF_DIRTY]) { + printk("Dirty list empty %d\n", i); + break; + } + + /* Clean buffer on dirty list? Refile it */ + if (!buffer_dirty(bh)) { + refile_buffer(bh); + continue; + } + + if (buffer_locked(bh)) + continue; + /* Should we write back buffers that are + shared or not?? Currently dirty buffers + are not shared, so it does not matter */ + next->b_count++; + bh->b_count++; + ndirty++; + bh->b_flushtime = 0; + if (MAJOR(bh->b_dev) == LOOP_MAJOR) { + ll_rw_block(wrta_cmd,1, &bh); + wrta_cmd = WRITEA; + if (buffer_dirty(bh)) + --ndirty; + } + else + ll_rw_block(WRITE, 1, &bh); + bh->b_count--; + next->b_count--; + } + /* If we didn't write anything, but there are still + * dirty buffers, then make the next write to a + * loop device to be a blocking write. + * This lets us block--which we _must_ do! */ + if (ndirty == 0 + && nr_buffers_type[BUF_DIRTY] > 0 && wrta_cmd != WRITE) { + wrta_cmd = WRITE; + goto restart; } - 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); + printk("wrote %d/%d buffers...", nwritten, ndirty); #endif run_task_queue(&tq_disk); - return 0; } @@ -1669,10 +1687,12 @@ asmlinkage int sys_bdflush(int func, long data) if (!capable(CAP_SYS_ADMIN)) goto out; - if (func == 1) { - error = sync_old_buffers(); - goto out; - } + if (func == 1) + /* Func 1 used to call sync_old_buffers; a user space + daemon would call it periodically. This is no + longer necessary. Returning -EPERM here makes the + daemon silently exit. */ + goto out; /* Basically func 1 means read param 1, 2 means write param 1, etc */ if (func >= 2) { @@ -1701,27 +1721,17 @@ out: return error; } -/* This is the actual bdflush daemon itself. It used to be started from - * the syscall above, but now we launch it ourselves internally with - * kernel_thread(...) directly after the first thread in init/main.c */ +/* This is the actual bdflush daemon itself. It used to be started + * from the syscall above, but now we launch it ourselves internally + * with kernel_thread(...) directly after the first thread in + * init/main.c. Every so often, or when woken up by another task that + * needs memory, we call sync_old_buffers to partially clear the dirty list. + */ -/* To prevent deadlocks for a loop device: - * 1) Do non-blocking writes to loop (avoids deadlock with running - * out of request blocks). - * 2) But do a blocking write if the only dirty buffers are loop buffers - * (otherwise we go into an infinite busy-loop). - * 3) Quit writing loop blocks if a freelist went low (avoids deadlock - * with running out of free buffers for loop's "real" device). -*/ int bdflush(void * unused) { - int i; - int ndirty; - int nlist; - int ncount; - struct buffer_head * bh, *next; - int major; - int wrta_cmd = WRITEA; /* non-blocking write for LOOP */ + long remaining = HZ * bdf_prm.b_un.interval; + struct task_struct *tsk = current; /* * We have a bare-bones task_struct, and really should fill @@ -1729,10 +1739,12 @@ int bdflush(void * unused) * display semi-sane things. Not real crucial though... */ - current->session = 1; - current->pgrp = 1; - sprintf(current->comm, "kflushd"); - bdflush_tsk = current; + tsk->session = 1; + tsk->pgrp = 1; + tsk->dumpable = 0; /* inhibit ptrace() */ + strcpy(tsk->comm, "kflushd"); + sigfillset(&tsk->blocked); + bdflush_tsk = tsk; /* * As a kernel thread we want to tamper with system buffers @@ -1742,88 +1754,36 @@ int bdflush(void * unused) lock_kernel(); for (;;) { + tsk->state = TASK_INTERRUPTIBLE; + remaining = schedule_timeout(remaining); + #ifdef DEBUG printk("bdflush() activated..."); #endif - CHECK_EMERGENCY_SYNC - ncount = 0; -#ifdef DEBUG - for(nlist = 0; nlist < NR_LIST; nlist++) -#else - for(nlist = BUF_DIRTY; nlist <= BUF_DIRTY; nlist++) -#endif - { - ndirty = 0; - repeat: - - bh = lru_list[nlist]; - if(bh) - for (i = nr_buffers_type[nlist]; i-- > 0 && ndirty < bdf_prm.b_un.ndirty; - bh = next) { - /* We may have stalled while waiting for I/O to complete. */ - if(bh->b_list != nlist) goto repeat; - next = bh->b_next_free; - if(!lru_list[nlist]) { - printk("Dirty list empty %d\n", i); - break; - } - - /* Clean buffer on dirty list? Refile it */ - if (nlist == BUF_DIRTY && !buffer_dirty(bh) && !buffer_locked(bh)) - { - refile_buffer(bh); - continue; - } - - if (buffer_locked(bh) || !buffer_dirty(bh)) - continue; - major = MAJOR(bh->b_dev); - /* Should we write back buffers that are shared or not?? - currently dirty buffers are not shared, so it does not matter */ - next->b_count++; - bh->b_count++; - ndirty++; - bh->b_flushtime = 0; - if (major == LOOP_MAJOR) { - ll_rw_block(wrta_cmd,1, &bh); - wrta_cmd = WRITEA; - if (buffer_dirty(bh)) - --ndirty; - } - else - ll_rw_block(WRITE, 1, &bh); -#ifdef DEBUG - if(nlist != BUF_DIRTY) ncount++; -#endif - bh->b_count--; - next->b_count--; - } - } + if (remaining == 0) { + /* + * Also try to flush inodes and supers, since + * otherwise there would be no way of ensuring + * that these quantities ever get written + * back. Ideally, we would have a timestamp + * on the inodes and superblocks so that we + * could write back only the old ones. + */ + sync_supers(0); + sync_inodes(0); + remaining = HZ * bdf_prm.b_un.interval; + } + + /* Keep flushing till there aren't very many dirty buffers */ + do { + sync_old_buffers(); + } while(nr_buffers_type[BUF_DIRTY] > nr_buffers * bdf_prm.b_un.nfract/100); + + wake_up(&bdflush_done); #ifdef DEBUG - if (ncount) printk("sys_bdflush: %d dirty buffers not on dirty list\n", ncount); printk("sleeping again.\n"); #endif - /* If we didn't write anything, but there are still - * dirty buffers, then make the next write to a - * loop device to be a blocking write. - * This lets us block--which we _must_ do! */ - if (ndirty == 0 && nr_buffers_type[BUF_DIRTY] > 0 && wrta_cmd != WRITE) { - wrta_cmd = WRITE; - continue; - } - run_task_queue(&tq_disk); - wake_up(&bdflush_done); - - /* If there are still a lot of dirty buffers around, skip the sleep - and flush some more */ - if(ndirty == 0 || nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) { - spin_lock_irq(¤t->sigmask_lock); - flush_signals(current); - spin_unlock_irq(¤t->sigmask_lock); - - interruptible_sleep_on(&bdflush_wait); - } } } diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 7ba943cd6..b3c0195a3 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -29,7 +29,7 @@ /* dir inode-ops */ static int coda_create(struct inode *dir, struct dentry *new, int mode); static int coda_mknod(struct inode *dir, struct dentry *new, int mode, int rdev); -static int coda_lookup(struct inode *dir, struct dentry *target); +static struct dentry *coda_lookup(struct inode *dir, struct dentry *target); static int coda_link(struct dentry *old_dentry, struct inode *dir_inode, struct dentry *entry); static int coda_unlink(struct inode *dir_inode, struct dentry *entry); @@ -44,7 +44,7 @@ static int coda_rename(struct inode *old_inode, struct dentry *old_dentry, static int coda_readdir(struct file *file, void *dirent, filldir_t filldir); /* dentry ops */ -static int coda_dentry_revalidate(struct dentry *de); +static int coda_dentry_revalidate(struct dentry *de, int); static void coda_dentry_delete(struct dentry *); /* support routines */ @@ -107,7 +107,7 @@ struct file_operations coda_dir_operations = { /* inode operations for directories */ /* acces routines: lookup, readlink, permission */ -static int coda_lookup(struct inode *dir, struct dentry *entry) +static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry) { struct coda_inode_info *dircnp; struct inode *res_inode = NULL; @@ -125,16 +125,9 @@ static int coda_lookup(struct inode *dir, struct dentry *entry) if ( length > CODA_MAXNAMLEN ) { printk("name too long: lookup, %s (%*s)\n", coda_f2s(&dircnp->c_fid), length, name); - return -ENAMETOOLONG; + return ERR_PTR(-ENAMETOOLONG); } - - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("coda_lookup: inode is NULL or not a directory\n"); - return -ENOTDIR; - } - - CDEBUG(D_INODE, "name %s, len %d in ino %ld, fid %s\n", name, length, dir->i_ino, coda_f2s(&dircnp->c_fid)); @@ -160,11 +153,11 @@ static int coda_lookup(struct inode *dir, struct dentry *entry) } error = coda_cnode_make(&res_inode, &resfid, dir->i_sb); if (error) - return error; + return ERR_PTR(error); } else if (error != -ENOENT) { CDEBUG(D_INODE, "error for %s(%*s)%d\n", coda_f2s(&dircnp->c_fid), length, name, error); - return error; + return ERR_PTR(error); } CDEBUG(D_INODE, "lookup: %s is (%s), type %d result %d, dropme %d\n", name, coda_f2s(&resfid), type, error, dropme); @@ -178,7 +171,7 @@ exit: ITOC(res_inode)->c_flags |= C_VATTR; } EXIT; - return 0; + return NULL; } @@ -236,22 +229,11 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode) CDEBUG(D_INODE, "name: %s, length %d, mode %o\n",name, length, mode); - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("coda_create: inode is null or not a directory\n"); - return -ENOENT; - } - if (coda_isroot(dir) && coda_iscontrol(name, length)) return -EPERM; dircnp = ITOC(dir); - if ( length > CODA_MAXNAMLEN ) { - printk("name too long: create, %s(%s)\n", - coda_f2s(&dircnp->c_fid), name); - return -ENAMETOOLONG; - } - error = venus_create(dir->i_sb, &(dircnp->c_fid), name, length, 0, mode, 0, &newfid, &attrs); @@ -292,22 +274,11 @@ static int coda_mknod(struct inode *dir, struct dentry *de, int mode, int rdev) CDEBUG(D_INODE, "name: %s, length %d, mode %o, rdev %x\n",name, length, mode, rdev); - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("coda_mknod: inode is null or not a directory\n"); - return -ENOENT; - } - if (coda_isroot(dir) && coda_iscontrol(name, length)) return -EPERM; dircnp = ITOC(dir); - if ( length > CODA_MAXNAMLEN ) { - printk("name too long: mknod, %s(%s)\n", - coda_f2s(&dircnp->c_fid), name); - return -ENAMETOOLONG; - } - error = venus_create(dir->i_sb, &(dircnp->c_fid), name, length, 0, mode, rdev, &newfid, &attrs); @@ -344,14 +315,6 @@ static int coda_mkdir(struct inode *dir, struct dentry *de, int mode) ENTRY; coda_vfs_stat.mkdir++; - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("coda_mkdir: inode is NULL or not a directory\n"); - return -ENOENT; - } - - if ( len > CODA_MAXNAMLEN ) - return -ENAMETOOLONG; - if (coda_isroot(dir) && coda_iscontrol(name, len)) return -EPERM; @@ -409,11 +372,6 @@ static int coda_link(struct dentry *source_de, struct inode *dir_inode, CDEBUG(D_INODE, "old: fid: %s\n", coda_f2s(&(cnp->c_fid))); CDEBUG(D_INODE, "directory: %s\n", coda_f2s(&(dir_cnp->c_fid))); - if ( len > CODA_MAXNAMLEN ) { - printk("coda_link: name too long. \n"); - return -ENAMETOOLONG; - } - error = venus_link(dir_inode->i_sb,&(cnp->c_fid), &(dir_cnp->c_fid), (const char *)name, len); @@ -448,9 +406,6 @@ static int coda_symlink(struct inode *dir_inode, struct dentry *de, if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) return -EPERM; - if ( len > CODA_MAXNAMLEN ) - return -ENAMETOOLONG; - symlen = strlen(symname); if ( symlen > CODA_MAXPATHLEN ) return -ENAMETOOLONG; @@ -513,15 +468,8 @@ int coda_rmdir(struct inode *dir, struct dentry *de) ENTRY; coda_vfs_stat.rmdir++; - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("coda_rmdir: inode is NULL or not a directory\n"); - return -ENOENT; - } dircnp = ITOC(dir); - if (len > CODA_MAXNAMLEN) - return -ENAMETOOLONG; - if (!list_empty(&de->d_hash)) return -EBUSY; error = venus_rmdir(dir->i_sb, &(dircnp->c_fid), name, len); @@ -545,7 +493,6 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, const char *new_name = new_dentry->d_name.name; int old_length = old_dentry->d_name.len; int new_length = new_dentry->d_name.len; - struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; struct coda_inode_info *new_cnp, *old_cnp; int error; @@ -553,10 +500,6 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, ENTRY; coda_vfs_stat.rename++; - if ( (old_length > CODA_MAXNAMLEN) || new_length > CODA_MAXNAMLEN ) { - return -ENAMETOOLONG; - } - old_cnp = ITOC(old_dir); new_cnp = ITOC(new_dir); @@ -565,21 +508,6 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, old_name, old_length, strlen(old_name), new_name, new_length, strlen(new_name),old_dentry->d_count, new_dentry->d_count); - if (new_inode == old_inode) - return 0; - - /* make sure target is not in use */ - if (new_inode && S_ISDIR(new_inode->i_mode)) { - /* - * Prune any children before testing for busy. - */ - if (new_dentry->d_count > 1) - shrink_dcache_parent(new_dentry); - - if (new_dentry->d_count > 1) - return -EBUSY; - } - /* the C library will do unlink/create etc */ if ( coda_crossvol_rename == 0 && old_cnp->c_fid.Volume != new_cnp->c_fid.Volume ) @@ -599,7 +527,6 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, coda_flag_inode(new_dir, C_VATTR); CDEBUG(D_INODE, "result %d\n", error); - d_move(old_dentry, new_dentry); EXIT; return 0; @@ -619,11 +546,6 @@ int coda_readdir(struct file *file, void *dirent, filldir_t filldir) ENTRY; coda_vfs_stat.readdir++; - if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) { - printk("coda_readdir: inode is NULL or not a directory\n"); - return -EBADF; - } - cnp = ITOC(inode); if ( !cnp->c_ovp ) { CDEBUG(D_FILE, "open inode pointer = NULL.\n"); @@ -637,7 +559,9 @@ int coda_readdir(struct file *file, void *dirent, filldir_t filldir) result = coda_venus_readdir(&open_file, dirent, filldir); } else { /* potemkin case: we are handed a directory inode */ + down(&cnp->c_ovp->i_sem); result = open_file.f_op->readdir(&open_file, dirent, filldir); + up(&cnp->c_ovp->i_sem); } coda_restore_codafile(inode, file, cnp->c_ovp, &open_file); EXIT; @@ -854,7 +778,7 @@ exit: } /* called when a cache lookup succeeds */ -static int coda_dentry_revalidate(struct dentry *de) +static int coda_dentry_revalidate(struct dentry *de, int flags) { int valid = 1; struct inode *inode = de->d_inode; diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 9c0846b02..55eed097a 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -23,9 +23,6 @@ #include <linux/fs.h> #include <linux/stat.h> -#include <linux/errno.h> -#include <linux/locks.h> -#include <linux/string.h> #include <asm/uaccess.h> #include <linux/vmalloc.h> #include <asm/segment.h> diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c index 8f3bd532a..79a4c7ebb 100644 --- a/fs/coda/sysctl.c +++ b/fs/coda/sysctl.c @@ -24,6 +24,8 @@ #include <asm/segment.h> #include <asm/uaccess.h> #include <linux/utsname.h> +#define __NO_VERSION__ +#include <linux/module.h> #include <linux/coda.h> #include <linux/coda_linux.h> @@ -491,6 +493,14 @@ struct proc_dir_entry proc_coda_cache_inv = { coda_cache_inv_stats_get_info }; +static void coda_proc_modcount(struct inode *inode, int fill) +{ + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +} + #endif @@ -504,6 +514,7 @@ void coda_sysctl_init() #ifdef CONFIG_PROC_FS proc_register(&proc_root_fs,&proc_fs_coda); + proc_fs_coda.fill_inode = &coda_proc_modcount; proc_register(&proc_fs_coda,&proc_coda_vfs); proc_register(&proc_fs_coda,&proc_coda_upcall); proc_register(&proc_fs_coda,&proc_coda_permission); diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 4c640892d..d3f0161a3 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -30,7 +30,6 @@ #include <linux/string.h> #include <asm/uaccess.h> #include <linux/vmalloc.h> -#include <asm/segment.h> #include <linux/coda.h> #include <linux/coda_linux.h> diff --git a/fs/dcache.c b/fs/dcache.c index 51c7869b3..aa299f61e 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2,7 +2,8 @@ * fs/dcache.c * * Complete reimplementation - * (C) 1997 Thomas Schoebel-Theuer + * (C) 1997 Thomas Schoebel-Theuer, + * with heavy changes by Linus Torvalds */ /* @@ -470,7 +471,12 @@ void shrink_dcache_parent(struct dentry * parent) */ void shrink_dcache_memory(int priority, unsigned int gfp_mask) { - prune_dcache(0); + if (gfp_mask & __GFP_IO) { + int count = 0; + if (priority) + count = dentry_stat.nr_unused / priority; + prune_dcache(count); + } } #define NAME_ALLOC_LEN(len) ((len+16) & ~15) diff --git a/fs/devpts/root.c b/fs/devpts/root.c index f517367b9..c284f1d97 100644 --- a/fs/devpts/root.c +++ b/fs/devpts/root.c @@ -17,8 +17,8 @@ #include "devpts_i.h" static int devpts_root_readdir(struct file *,void *,filldir_t); -static int devpts_root_lookup(struct inode *,struct dentry *); -static int devpts_revalidate(struct dentry *); +static struct dentry *devpts_root_lookup(struct inode *,struct dentry *); +static int devpts_revalidate(struct dentry *, int); static struct file_operations devpts_root_operations = { NULL, /* llseek */ @@ -81,9 +81,6 @@ static int devpts_root_readdir(struct file *filp, void *dirent, filldir_t filldi off_t nr; char numbuf[16]; - if (!inode || !S_ISDIR(inode->i_mode)) - return -ENOTDIR; - nr = filp->f_pos; switch(nr) @@ -119,7 +116,7 @@ static int devpts_root_readdir(struct file *filp, void *dirent, filldir_t filldi * the pty really does still exist. Never revalidate negative dentries; * for simplicity (fix later?) */ -static int devpts_revalidate(struct dentry * dentry) +static int devpts_revalidate(struct dentry * dentry, int flags) { struct devpts_sb_info *sbi; @@ -131,42 +128,39 @@ static int devpts_revalidate(struct dentry * dentry) return ( sbi->inodes[dentry->d_inode->i_ino - 2] == dentry->d_inode ); } -static int devpts_root_lookup(struct inode * dir, struct dentry * dentry) +static struct dentry *devpts_root_lookup(struct inode * dir, struct dentry * dentry) { struct devpts_sb_info *sbi = SBI(dir->i_sb); unsigned int entry; int i; const char *p; - if (!S_ISDIR(dir->i_mode)) - return -ENOTDIR; - dentry->d_inode = NULL; /* Assume failure */ dentry->d_op = &devpts_dentry_operations; if ( dentry->d_name.len == 1 && dentry->d_name.name[0] == '0' ) { entry = 0; } else if ( dentry->d_name.len < 1 ) { - return 0; + return NULL; } else { p = dentry->d_name.name; if ( *p < '1' || *p > '9' ) - return 0; + return NULL; entry = *p++ - '0'; for ( i = dentry->d_name.len-1 ; i ; i-- ) { unsigned int nentry = *p++ - '0'; if ( nentry > 9 ) - return 0; + return NULL; nentry += entry * 10; if (nentry < entry) - return 0; + return NULL; entry = nentry; } } if ( entry >= sbi->max_ptys ) - return 0; + return NULL; dentry->d_inode = sbi->inodes[entry]; if ( dentry->d_inode ) @@ -174,5 +168,5 @@ static int devpts_root_lookup(struct inode * dir, struct dentry * dentry) d_add(dentry, dentry->d_inode); - return 0; + return NULL; } diff --git a/fs/dquot.c b/fs/dquot.c index 8b7d07295..76630352f 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -483,7 +483,10 @@ got_it: if (dquot->dq_flags & (DQ_LOCKED | DQ_MOD)) { wait_on_dquot(dquot); if (dquot->dq_flags & DQ_MOD) - write_dquot(dquot); + { + if(dquot->dq_mnt != (struct vfsmount *)NULL) + write_dquot(dquot); + } /* * The dquot may be back in use now, so we * must recheck the free list. @@ -1265,11 +1268,11 @@ out: int quota_on(kdev_t dev, short type, char *path) { - struct file *filp = NULL; - struct dentry *dentry; + struct file *f; struct vfsmount *vfsmnt; struct inode *inode; struct dquot *dquot; + struct quota_mount_options *mnt_dquot; char *tmp; int error; @@ -1278,69 +1281,48 @@ int quota_on(kdev_t dev, short type, char *path) return -ENODEV; if (is_enabled(vfsmnt, type)) - return(-EBUSY); + return -EBUSY; + mnt_dquot = &vfsmnt->mnt_dquot; tmp = getname(path); error = PTR_ERR(tmp); if (IS_ERR(tmp)) return error; - dentry = open_namei(tmp, O_RDWR, 0600); + f = filp_open(tmp, O_RDWR, 0600); putname(tmp); + if (IS_ERR(f)) + return PTR_ERR(f); + + /* sanity checks */ + error = -EIO; + if (!f->f_op->read && !f->f_op->write) + goto cleanup; + inode = f->f_dentry->d_inode; + error = -EACCES; + if (!S_ISREG(inode->i_mode)) + goto cleanup; + error = -EINVAL; + if (inode->i_size == 0 || (inode->i_size % sizeof(struct dqblk)) != 0) + goto cleanup; + + /* OK, there we go */ + set_enable_flags(vfsmnt, type); + mnt_dquot->files[type] = f; + + dquot = dqget(dev, 0, type); + mnt_dquot->inode_expire[type] = (dquot) ? dquot->dq_itime : MAX_IQ_TIME; + mnt_dquot->block_expire[type] = (dquot) ? dquot->dq_btime : MAX_DQ_TIME; + dqput(dquot); - error = PTR_ERR(dentry); - if (IS_ERR(dentry)) - return error; - inode = dentry->d_inode; - - if (!S_ISREG(inode->i_mode)) { - dput(dentry); - return -EACCES; - } - - if (inode->i_size == 0 || (inode->i_size % sizeof(struct dqblk)) != 0) { - dput(dentry); - return(-EINVAL); - } - - filp = get_empty_filp(); - if (filp != (struct file *)NULL) { - filp->f_mode = (O_RDWR + 1) & O_ACCMODE; - filp->f_flags = O_RDWR; - 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) { - error = get_write_access(inode); - if (!error) { - if (filp->f_op && filp->f_op->open) - error = filp->f_op->open(inode, filp); - if (!error) { - set_enable_flags(vfsmnt, type); - vfsmnt->mnt_dquot.files[type] = filp; - - dquot = dqget(dev, 0, type); - vfsmnt->mnt_dquot.inode_expire[type] = (dquot) ? dquot->dq_itime : MAX_IQ_TIME; - vfsmnt->mnt_dquot.block_expire[type] = (dquot) ? dquot->dq_btime : MAX_DQ_TIME; - dqput(dquot); - - vfsmnt->mnt_sb->dq_op = &dquot_operations; - add_dquot_ref(dev, type); - - return(0); - } - put_write_access(inode); - } - } else - error = -EIO; - put_filp(filp); - } else - error = -EMFILE; + vfsmnt->mnt_sb->dq_op = &dquot_operations; + add_dquot_ref(dev, type); - dput(dentry); + return(0); - return(error); +cleanup: + fput(f); + return error; } /* @@ -1367,8 +1349,8 @@ asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr) case Q_GETSTATS: break; case Q_GETQUOTA: - if (((type == USRQUOTA && current->uid != id) || - (type == GRPQUOTA && current->gid != id)) && + if (((type == USRQUOTA && current->euid != id) || + (type == GRPQUOTA && current->egid != id)) && !capable(CAP_SYS_RESOURCE)) goto out; break; @@ -29,7 +29,6 @@ #include <linux/a.out.h> #include <linux/stat.h> #include <linux/fcntl.h> -#include <linux/user.h> #include <linux/smp_lock.h> #include <linux/init.h> diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 22af55334..a6753d276 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -115,8 +115,6 @@ static int ext2_readdir(struct file * filp, int err; struct inode *inode = filp->f_dentry->d_inode; - if (!inode || !S_ISDIR(inode->i_mode)) - return -EBADF; sb = inode->i_sb; stored = 0; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index f0f2ca98f..8bd5c9684 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -286,7 +286,8 @@ static struct buffer_head * block_getblk (struct inode * inode, u32 * p; struct buffer_head * result; int blocks = inode->i_sb->s_blocksize / 512; - + unsigned long limit; + if (!bh) return NULL; if (!buffer_uptodate(bh)) { @@ -309,13 +310,22 @@ repeat: brelse (result); goto repeat; } - if (!create || new_block >= - (current->rlim[RLIMIT_FSIZE].rlim_cur >> - EXT2_BLOCK_SIZE_BITS(inode->i_sb))) { + *err = -EFBIG; + if (!create) { brelse (bh); - *err = -EFBIG; return NULL; } + + limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit < RLIM_INFINITY) { + limit >>= EXT2_BLOCK_SIZE_BITS(inode->i_sb); + if (new_block >= limit) { + brelse (bh); + send_sig(SIGXFSZ, current, 0); + return NULL; + } + } + if (inode->u.ext2_i.i_next_alloc_block == new_block) goal = inode->u.ext2_i.i_next_alloc_goal; if (!goal) { @@ -697,7 +707,7 @@ static int ext2_update_inode(struct inode * inode, int do_sync) if (buffer_req(bh) && !buffer_uptodate(bh)) { printk ("IO error syncing ext2 inode [" "%s:%08lx]\n", - kdevname(inode->i_dev), inode->i_ino); + bdevname(inode->i_dev), inode->i_ino); err = -EIO; } } diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 5f66f5dab..7a309fa77 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -74,8 +74,6 @@ static struct buffer_head * ext2_find_entry (struct inode * dir, int block, toread, i, err; *res_dir = NULL; - if (!dir) - return NULL; sb = dir->i_sb; if (namelen > EXT2_NAME_LEN) @@ -168,14 +166,14 @@ failure: return NULL; } -int ext2_lookup(struct inode * dir, struct dentry *dentry) +struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry) { struct inode * inode; struct ext2_dir_entry_2 * de; struct buffer_head * bh; if (dentry->d_name.len > EXT2_NAME_LEN) - return -ENAMETOOLONG; + return ERR_PTR(-ENAMETOOLONG); bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); inode = NULL; @@ -185,10 +183,10 @@ int ext2_lookup(struct inode * dir, struct dentry *dentry) inode = iget(dir->i_sb, ino); if (!inode) - return -EACCES; + return ERR_PTR(-EACCES); } d_add(dentry, inode); - return 0; + return NULL; } /* @@ -218,12 +216,6 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, return NULL; sb = dir->i_sb; - if (namelen > EXT2_NAME_LEN) - { - *err = -ENAMETOOLONG; - return NULL; - } - if (!namelen) return NULL; /* @@ -409,10 +401,6 @@ int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) struct ext2_dir_entry_2 * de; int err = -EIO; - err = -ENAMETOOLONG; - if (dentry->d_name.len > EXT2_NAME_LEN) - goto out; - inode = ext2_new_inode (dir, mode, &err); if (!inode) goto out; @@ -474,10 +462,6 @@ int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) struct ext2_dir_entry_2 * de; int err; - err = -ENAMETOOLONG; - if (dentry->d_name.len > EXT2_NAME_LEN) - goto out; - err = -EMLINK; if (dir->i_nlink >= EXT2_LINK_MAX) goto out; @@ -618,10 +602,6 @@ int ext2_rmdir (struct inode * dir, struct dentry *dentry) struct buffer_head * bh; struct ext2_dir_entry_2 * de; - retval = -ENAMETOOLONG; - if (dentry->d_name.len > EXT2_NAME_LEN) - goto out; - retval = -ENOENT; bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); if (!bh) @@ -634,14 +614,12 @@ int ext2_rmdir (struct inode * dir, struct dentry *dentry) if (le32_to_cpu(de->inode) != inode->i_ino) goto end_rmdir; + retval = -ENOTEMPTY; if (!empty_dir (inode)) - retval = -ENOTEMPTY; - else if (le32_to_cpu(de->inode) != inode->i_ino) - retval = -ENOENT; - else { - retval = ext2_delete_entry (de, bh); - dir->i_version = ++event; - } + goto end_rmdir; + + retval = ext2_delete_entry (de, bh); + dir->i_version = ++event; if (retval) goto end_rmdir; mark_buffer_dirty(bh, 1); @@ -665,7 +643,6 @@ int ext2_rmdir (struct inode * dir, struct dentry *dentry) end_rmdir: brelse (bh); -out: return retval; } @@ -676,10 +653,6 @@ int ext2_unlink(struct inode * dir, struct dentry *dentry) struct buffer_head * bh; struct ext2_dir_entry_2 * de; - retval = -ENAMETOOLONG; - if (dentry->d_name.len > EXT2_NAME_LEN) - goto out; - retval = -ENOENT; bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); if (!bh) @@ -718,7 +691,6 @@ int ext2_unlink(struct inode * dir, struct dentry *dentry) end_unlink: brelse (bh); -out: return retval; } @@ -805,9 +777,6 @@ int ext2_link (struct dentry * old_dentry, if (S_ISDIR(inode->i_mode)) return -EPERM; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - return -EPERM; - if (inode->i_nlink >= EXT2_LINK_MAX) return -EMLINK; @@ -851,17 +820,10 @@ int ext2_link (struct dentry * old_dentry, le16_to_cpu(((struct ext2_dir_entry_2 *) buffer)->rec_len)))->inode /* - * rename uses retrying to avoid race-conditions: at least they should be - * minimal. - * it tries to allocate all the blocks, then sanity-checks, and if the sanity- - * checks fail, it tries to restart itself again. Very practical - no changes - * are done until we know everything works ok.. and then all the changes can be - * done in one fell swoop when we have claimed all the buffers needed. - * * 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, struct dentry *old_dentry, +int ext2_rename (struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry) { struct inode * old_inode, * new_inode; @@ -870,9 +832,6 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry, int retval; old_bh = new_bh = dir_bh = NULL; - retval = -ENAMETOOLONG; - if (old_dentry->d_name.len > EXT2_NAME_LEN) - goto end_rename; old_bh = ext2_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); /* @@ -897,24 +856,13 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry, DQUOT_INIT(new_inode); } } - retval = 0; - if (new_inode == old_inode) - goto end_rename; if (S_ISDIR(old_inode->i_mode)) { - retval = -EINVAL; - if (is_subdir(new_dentry, old_dentry)) - goto end_rename; if (new_inode) { - /* Prune any children before testing for busy */ - if (new_dentry->d_count > 1) - shrink_dcache_parent(new_dentry); - retval = -EBUSY; - if (new_dentry->d_count > 1) - goto end_rename; retval = -ENOTEMPTY; if (!empty_dir (new_inode)) goto end_rename; } + retval = -EIO; dir_bh = ext2_bread (old_inode, 0, 0, &retval); if (!dir_bh) goto end_rename; @@ -977,8 +925,6 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry, wait_on_buffer (new_bh); } - /* Update the dcache */ - d_move(old_dentry, new_dentry); retval = 0; end_rename: @@ -987,30 +933,3 @@ end_rename: brelse (new_bh); return retval; } - -/* - * Ok, rename also locks out other renames, as they can change the parent of - * a directory, and we don't want any races. Other races are checked for by - * "do_rename()", which restarts if there are inconsistencies. - * - * Note that there is no race between different filesystems: it's only within - * the same device that races occur: many renames can happen at once, as long - * as they are on different partitions. - * - * In the second extended file system, we use a lock flag stored in the memory - * 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, 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_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 674a5043c..b7c08a009 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -56,9 +56,9 @@ void ext2_error (struct super_block * sb, const char * function, (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_PANIC && !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO))) panic ("EXT2-fs panic (device %s): %s: %s\n", - kdevname(sb->s_dev), function, error_buf); + bdevname(sb->s_dev), function, error_buf); printk (KERN_CRIT "EXT2-fs error (device %s): %s: %s\n", - kdevname(sb->s_dev), function, error_buf); + bdevname(sb->s_dev), function, error_buf); if (test_opt (sb, ERRORS_RO) || (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_RO && !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) { @@ -87,7 +87,7 @@ NORET_TYPE void ext2_panic (struct super_block * sb, const char * function, sb->s_lock=0; sb->s_flags |= MS_RDONLY; panic ("EXT2-fs panic (device %s): %s: %s\n", - kdevname(sb->s_dev), function, error_buf); + bdevname(sb->s_dev), function, error_buf); } void ext2_warning (struct super_block * sb, const char * function, @@ -99,7 +99,7 @@ void ext2_warning (struct super_block * sb, const char * function, vsprintf (error_buf, fmt, args); va_end (args); printk (KERN_WARNING "EXT2-fs warning (device %s): %s: %s\n", - kdevname(sb->s_dev), function, error_buf); + bdevname(sb->s_dev), function, error_buf); } void ext2_put_super (struct super_block * sb) @@ -444,7 +444,7 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, if (sb->s_magic != EXT2_SUPER_MAGIC) { if (!silent) printk ("VFS: Can't find an ext2 filesystem on dev " - "%s.\n", kdevname(dev)); + "%s.\n", bdevname(dev)); failed_mount: sb->s_dev = 0; unlock_super (sb); @@ -457,14 +457,14 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, if (le32_to_cpu(es->s_feature_incompat) & ~EXT2_FEATURE_INCOMPAT_SUPP) { printk("EXT2-fs: %s: couldn't mount because of " "unsupported optional features.\n", - kdevname(dev)); + bdevname(dev)); goto failed_mount; } if (!(sb->s_flags & MS_RDONLY) && (le32_to_cpu(es->s_feature_ro_compat) & ~EXT2_FEATURE_RO_COMPAT_SUPP)) { printk("EXT2-fs: %s: couldn't mount RDWR because of " "unsupported optional features.\n", - kdevname(dev)); + bdevname(dev)); goto failed_mount; } } @@ -543,8 +543,6 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, else sb->u.ext2_sb.s_resgid = le16_to_cpu(es->s_def_resgid); sb->u.ext2_sb.s_mount_state = le16_to_cpu(es->s_state); - sb->u.ext2_sb.s_rename_lock = 0; - sb->u.ext2_sb.s_rename_wait = NULL; sb->u.ext2_sb.s_addr_per_block_bits = log2 (EXT2_ADDR_PER_BLOCK(sb)); sb->u.ext2_sb.s_desc_per_block_bits = @@ -553,13 +551,13 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, if (!silent) printk ("VFS: Can't find an ext2 filesystem on dev " "%s.\n", - kdevname(dev)); + bdevname(dev)); goto failed_mount; } if (sb->s_blocksize != bh->b_size) { if (!silent) printk ("VFS: Unsupported blocksize on dev " - "%s.\n", kdevname(dev)); + "%s.\n", bdevname(dev)); goto failed_mount; } diff --git a/fs/fat/cvf.c b/fs/fat/cvf.c index 62b70b160..9dd70f8c5 100644 --- a/fs/fat/cvf.c +++ b/fs/fat/cvf.c @@ -1,8 +1,11 @@ -/* +/* * CVF extensions for fat-based filesystems * * written 1997,1998 by Frank Gockel <gockel@sent13.uni-duisburg.de> * + * please do not remove the next line, dmsdos needs it for verifying patches + * CVF-FAT-VERSION-ID: 1.2.0 + * */ #include<linux/sched.h> @@ -11,6 +14,10 @@ #include<linux/msdos_fs_sb.h> #include<linux/string.h> #include<linux/fat_cvf.h> +#include<linux/config.h> +#ifdef CONFIG_KMOD +#include<linux/kmod.h> +#endif #define MAX_CVF_FORMATS 3 @@ -95,6 +102,24 @@ int detect_cvf(struct super_block*sb,char*force) int found_i=-1; if(force) + if(strcmp(force,"autoload")==0) + { +#ifdef CONFIG_KMOD + request_module("cvf_autoload"); + force=NULL; +#else + printk("cannot autoload CVF modules: kmod support is not compiled into kernel\n"); + return -1; +#endif + } + +#ifdef CONFIG_KMOD + if(force) + if(*force) + request_module(force); +#endif + + if(force) { if(*force) { for(i=0;i<MAX_CVF_FORMATS;++i) { if(cvf_formats[i]) @@ -102,6 +127,8 @@ int detect_cvf(struct super_block*sb,char*force) return i; } } + printk("CVF format %s unknown (module not loaded?)\n",force); + return -1; } } @@ -115,6 +142,6 @@ int detect_cvf(struct super_block*sb,char*force) } if(found==1)return found_i; - if(found>1)printk("CVF detection ambiguous, use cvf_format=xxx option\n"); + if(found>1)printk("CVF detection ambiguous, please use cvf_format=xxx option\n"); return -1; } diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 1c44247a0..b6d6fb405 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -9,6 +9,7 @@ * * VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu> * Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk> + * Plugged buffer overrun in readdir(). AV */ #define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S)) @@ -59,6 +60,7 @@ struct file_operations fat_dir_operations = { * characters are a sort of uuencoded 16 bit Unicode value. This lets * us do a full dump and restore of Unicode filenames. We could get * into some trouble with long Unicode names, but ignore that right now. + * Ahem... Stack smashing in ring 0 isn't fun. Fixed. */ static int uni16_to_x8(unsigned char *ascii, unsigned char *uni, int uni_xlate, @@ -93,6 +95,11 @@ uni16_to_x8(unsigned char *ascii, unsigned char *uni, int uni_xlate, *op++ = '?'; } } + /* We have some slack there, so it's OK */ + if (op>ascii+256) { + op = ascii + 256; + break; + } } *op = 0; return (op - ascii); @@ -138,8 +145,6 @@ int fat_readdirx( unsigned char *unicode = NULL; struct nls_table *nls = MSDOS_SB(sb)->nls_io; - if (!inode || !S_ISDIR(inode->i_mode)) - return -EBADF; /* Fake . and .. for the root directory. */ if (inode->i_ino == MSDOS_ROOT_INO) { while (oldpos < 2) { @@ -186,9 +191,17 @@ int fat_readdirx( id = ds->id; if (id & 0x40) { slots = id & ~0x40; - long_slots = slots; - is_long = 1; - alias_checksum = ds->alias_checksum; + /* + * Dirty, but not dirtier than the original, + * and plugs the hole. + */ + if (slots > 20) + slots = 0; + else { + long_slots = slots; + is_long = 1; + alias_checksum = ds->alias_checksum; + } } get_new_entry = 1; diff --git a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c index f57db8bf3..838c679d4 100644 --- a/fs/fat/fatfs_syms.c +++ b/fs/fat/fatfs_syms.c @@ -54,6 +54,7 @@ EXPORT_SYMBOL(lock_fat); EXPORT_SYMBOL(unlock_fat); EXPORT_SYMBOL(fat_dir_ioctl); EXPORT_SYMBOL(fat_readpage); +EXPORT_SYMBOL(fat_is_binary); int init_fat_fs(void) { diff --git a/fs/fat/file.c b/fs/fat/file.c index 0ff5f187f..7c9518181 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -456,6 +456,10 @@ void fat_truncate(struct inode *inode) /* Why no return value? Surely the disk could fail... */ if (IS_IMMUTABLE(inode)) return /* -EPERM */; + if(inode->i_sb->s_flags&MS_RDONLY) { + printk("FAT: fat_truncate called though fs is read-only, uhh...\n"); + return /* -EROFS */; + } 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; diff --git a/fs/fat/inode.c b/fs/fat/inode.c index e018eb880..339bcb6f6 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -3,6 +3,10 @@ * * Written 1992,1993 by Werner Almesberger * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner + * + * Fixes: + * + * Max Cohan: Fixed invalid FSINFO offset when info_sector is 0 */ #include <linux/version.h> @@ -328,7 +332,6 @@ fat_read_super(struct super_block *sb, void *data, int silent) fat_brelse (sb, bh); goto out_no_bread; } - set_blocksize(sb->s_dev, blksize); /* * The DOS3 partition size limit is *not* 32M as many people think. @@ -362,8 +365,16 @@ fat_read_super(struct super_block *sb, void *data, int silent) fat32 = 1; MSDOS_SB(sb)->fat_length= CF_LE_W(b->fat32_length)*sector_mult; MSDOS_SB(sb)->root_cluster = CF_LE_L(b->root_cluster); - MSDOS_SB(sb)->fsinfo_offset = - CF_LE_W(b->info_sector) * logical_sector_size + 0x1e0; + + /* MC - if info_sector is 0, don't multiply by 0 */ + if(CF_LE_W(b->info_sector) == 0) { + MSDOS_SB(sb)->fsinfo_offset = + logical_sector_size + 0x1e0; + } else { + MSDOS_SB(sb)->fsinfo_offset = + (CF_LE_W(b->info_sector) * logical_sector_size) + + 0x1e0; + } if (MSDOS_SB(sb)->fsinfo_offset + sizeof(struct fat_boot_fsinfo) > sb->s_blocksize) { printk("fat_read_super: Bad fsinfo_offset\n"); fat_brelse(sb, bh); @@ -411,6 +422,7 @@ fat_read_super(struct super_block *sb, void *data, int silent) || !b->secs_track || !b->heads; } fat_brelse(sb, bh); + set_blocksize(sb->s_dev, blksize); /* This must be done after the brelse because the bh is a dummy allocated by fat_bread (see buffer.c) @@ -691,8 +703,8 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o if(raw_entry->attr & ATTR_SYS) if (MSDOS_SB(sb)->options.sys_immutable) inode->i_flags |= S_IMMUTABLE; - MSDOS_I(inode)->i_binary = is_binary(MSDOS_SB(sb)->options.conversion, - raw_entry->ext); + MSDOS_I(inode)->i_binary = + fat_is_binary(MSDOS_SB(sb)->options.conversion, raw_entry->ext); MSDOS_I(inode)->i_attrs = raw_entry->attr & ATTR_UNUSED; /* this is as close to the truth as we can get ... */ inode->i_blksize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE; diff --git a/fs/fat/misc.c b/fs/fat/misc.c index b398a0bbd..1dfddd3c7 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -51,11 +51,11 @@ void fat_fs_panic(struct super_block *s,const char *msg) /* - * is_binary selects optional text conversion based on the conversion mode and - * the extension part of the file name. + * fat_is_binary selects optional text conversion based on the conversion mode + * and the extension part of the file name. */ -int is_binary(char conversion,char *extension) +int fat_is_binary(char conversion,char *extension) { char *walk; diff --git a/fs/fat/mmap.c b/fs/fat/mmap.c index 4cda79196..0494ad013 100644 --- a/fs/fat/mmap.c +++ b/fs/fat/mmap.c @@ -113,8 +113,6 @@ int fat_mmap(struct file * file, struct vm_area_struct * vma) mark_inode_dirty(inode); } - vma->vm_file = file; - file->f_count++; vma->vm_ops = &fat_file_mmap; return 0; } diff --git a/fs/file_table.c b/fs/file_table.c index cbc33634a..f7679dba3 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -81,6 +81,8 @@ struct file * get_empty_filp(void) memset(f, 0, sizeof(*f)); f->f_count = 1; f->f_version = ++event; + f->f_uid = current->fsuid; + f->f_gid = current->fsgid; put_inuse(f); return f; } @@ -119,6 +121,8 @@ int init_private_file(struct file *filp, struct dentry *dentry, int mode) filp->f_mode = mode; filp->f_count = 1; filp->f_dentry = dentry; + filp->f_uid = current->fsuid; + filp->f_gid = current->fsgid; 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); diff --git a/fs/hfs/ChangeLog b/fs/hfs/ChangeLog index c0b077f47..f97bd0086 100644 --- a/fs/hfs/ChangeLog +++ b/fs/hfs/ChangeLog @@ -1,3 +1,13 @@ +1999-01-30 a sun <asun@hecate.darksunrising.blah> + + * catalog.c (hfs_cat_move): fixed corruption problem with + renames. + +1999-01-27 a sun <asun@hecate.darksunrising.blah> + + * file_hdr.c (get/set_dates): got rid of broken afpd times. NOTE: + you must use netatalk-1.4b2+asun2.1.2 or newer for this. + 1998-12-20 a sun <asun@hecate.darksunrising.blah> * bdelete.c (del_root): assign bthLNode and bthFNode only if the diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c index 306c018e7..48577a9e6 100644 --- a/fs/hfs/catalog.c +++ b/fs/hfs/catalog.c @@ -1348,7 +1348,7 @@ int hfs_cat_move(struct hfs_cat_entry *old_dir, struct hfs_cat_entry *new_dir, hfs_sleep_on(&mdb->rename_wait); } spin_lock(&entry_lock); - mdb->rename_lock = 1; + mdb->rename_lock = 1; /* XXX: should be atomic_inc */ spin_unlock(&entry_lock); /* keep readers from getting confused by changing dir size */ @@ -1385,7 +1385,6 @@ int hfs_cat_move(struct hfs_cat_entry *old_dir, struct hfs_cat_entry *new_dir, restart: /* see if the destination exists, getting it if it does */ dest = hfs_cat_get(mdb, new_key); - if (!dest) { /* destination doesn't exist, so create it */ struct hfs_cat_rec new_record; @@ -1408,14 +1407,16 @@ restart: goto bail3; } - /* build the new record */ + /* build the new record. make sure to zero out the + record. */ + memset(&new_record, 0, sizeof(new_record)); new_record.cdrType = entry->type; __write_entry(entry, &new_record); /* insert the new record */ error = hfs_binsert(mdb->cat_tree, HFS_BKEY(new_key), &new_record, is_dir ? 2 + sizeof(DIR_REC) : - 2 + sizeof(FIL_REC)); + 2 + sizeof(FIL_REC)); if (error == -EEXIST) { delete_entry(dest); unlock_entry(dest); @@ -1565,7 +1566,7 @@ done: } end_write(new_dir); spin_lock(&entry_lock); - mdb->rename_lock = 0; + mdb->rename_lock = 0; /* XXX: should use atomic_dec */ hfs_wake_up(&mdb->rename_wait); spin_unlock(&entry_lock); diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index f8d89227d..b355408c9 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -430,8 +430,6 @@ int hfs_rename(struct inode *old_dir, struct dentry *old_dentry, update_dirs_plus(new_parent, is_dir); } - /* update dcache */ - d_move(old_dentry, new_dentry); } hfs_rename_put: diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c index 03fa7f7ae..0ab81d966 100644 --- a/fs/hfs/dir_cap.c +++ b/fs/hfs/dir_cap.c @@ -27,7 +27,7 @@ /*================ Forward declarations ================*/ -static int cap_lookup(struct inode *, struct dentry *); +static struct dentry *cap_lookup(struct inode *, struct dentry *); static int cap_readdir(struct file *, void *, filldir_t); /*================ Global variables ================*/ @@ -147,7 +147,7 @@ struct inode_operations hfs_cap_rdir_inode_operations = { * inode corresponding to an entry in a directory, given the inode for * the directory and the name (and its length) of the entry. */ -static int cap_lookup(struct inode * dir, struct dentry *dentry) +static struct dentry *cap_lookup(struct inode * dir, struct dentry *dentry) { ino_t dtype; struct hfs_name cname; @@ -155,10 +155,6 @@ static int cap_lookup(struct inode * dir, struct dentry *dentry) struct hfs_cat_key key; struct inode *inode = NULL; - if (!dir || !S_ISDIR(dir->i_mode)) { - return -ENOENT; - } - dentry->d_op = &hfs_dentry_operations; entry = HFS_I(dir)->entry; dtype = HFS_ITYPE(dir->i_ino); @@ -207,7 +203,7 @@ static int cap_lookup(struct inode * dir, struct dentry *dentry) done: d_add(dentry, inode); - return 0; + return NULL; } /* diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c index 4e4e6fcc3..80e990627 100644 --- a/fs/hfs/dir_dbl.c +++ b/fs/hfs/dir_dbl.c @@ -23,7 +23,7 @@ /*================ Forward declarations ================*/ -static int dbl_lookup(struct inode *, struct dentry *); +static struct dentry *dbl_lookup(struct inode *, struct dentry *); static int dbl_readdir(struct file *, void *, filldir_t); static int dbl_create(struct inode *, struct dentry *, int); static int dbl_mkdir(struct inode *, struct dentry *, int); @@ -130,17 +130,13 @@ static int is_hdr(struct inode *dir, const char *name, int len) * the inode for the directory and the name (and its length) of the * entry. */ -static int dbl_lookup(struct inode * dir, struct dentry *dentry) +static struct dentry *dbl_lookup(struct inode * dir, struct dentry *dentry) { struct hfs_name cname; struct hfs_cat_entry *entry; struct hfs_cat_key key; struct inode *inode = NULL; - if (!dir || !S_ISDIR(dir->i_mode)) { - return -ENOENT; - } - dentry->d_op = &hfs_dentry_operations; entry = HFS_I(dir)->entry; @@ -173,7 +169,7 @@ static int dbl_lookup(struct inode * dir, struct dentry *dentry) done: d_add(dentry, inode); - return 0; + return NULL; } /* diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c index a4078e891..5cff9d814 100644 --- a/fs/hfs/dir_nat.c +++ b/fs/hfs/dir_nat.c @@ -29,7 +29,7 @@ /*================ Forward declarations ================*/ -static int nat_lookup(struct inode *, struct dentry *); +static struct dentry *nat_lookup(struct inode *, struct dentry *); static int nat_readdir(struct file *, void *, filldir_t); static int nat_rmdir(struct inode *, struct dentry *); static int nat_hdr_unlink(struct inode *, struct dentry *); @@ -136,7 +136,7 @@ struct inode_operations hfs_nat_hdir_inode_operations = { * the inode corresponding to an entry in a directory, given the inode * for the directory and the name (and its length) of the entry. */ -static int nat_lookup(struct inode * dir, struct dentry *dentry) +static struct dentry *nat_lookup(struct inode * dir, struct dentry *dentry) { ino_t dtype; struct hfs_name cname; @@ -144,10 +144,6 @@ static int nat_lookup(struct inode * dir, struct dentry *dentry) struct hfs_cat_key key; struct inode *inode = NULL; - if (!dir || !S_ISDIR(dir->i_mode)) { - return -ENOENT; - } - dentry->d_op = &hfs_dentry_operations; entry = HFS_I(dir)->entry; dtype = HFS_ITYPE(dir->i_ino); @@ -199,7 +195,7 @@ static int nat_lookup(struct inode * dir, struct dentry *dentry) done: d_add(dentry, inode); - return 0; + return NULL; } /* diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c index 3ae2d5b5d..9479fab08 100644 --- a/fs/hfs/file_hdr.c +++ b/fs/hfs/file_hdr.c @@ -303,16 +303,9 @@ static inline void adjust_forks(struct hfs_cat_entry *entry, static void get_dates(const struct hfs_cat_entry *entry, const struct inode *inode, hfs_u32 dates[3]) { - if (HFS_SB(inode->i_sb)->s_afpd) { - /* AFPD compatible: use un*x times */ - dates[0] = htonl(hfs_m_to_utime(entry->create_date)); - dates[1] = htonl(hfs_m_to_utime(entry->modify_date)); - dates[2] = htonl(hfs_m_to_utime(entry->backup_date)); - } else { - dates[0] = hfs_m_to_htime(entry->create_date); - dates[1] = hfs_m_to_htime(entry->modify_date); - dates[2] = hfs_m_to_htime(entry->backup_date); - } + dates[0] = hfs_m_to_htime(entry->create_date); + dates[1] = hfs_m_to_htime(entry->modify_date); + dates[2] = hfs_m_to_htime(entry->backup_date); } /* @@ -322,43 +315,23 @@ static void set_dates(struct hfs_cat_entry *entry, struct inode *inode, const hfs_u32 *dates) { hfs_u32 tmp; - if (HFS_SB(inode->i_sb)->s_afpd) { - /* AFPD compatible: use un*x times */ - tmp = hfs_u_to_mtime(ntohl(dates[0])); - if (entry->create_date != tmp) { - entry->create_date = tmp; - hfs_cat_mark_dirty(entry); - } - tmp = hfs_u_to_mtime(ntohl(dates[1])); - if (entry->modify_date != tmp) { - entry->modify_date = tmp; - inode->i_ctime = inode->i_atime = inode->i_mtime = - ntohl(dates[1]); - hfs_cat_mark_dirty(entry); - } - tmp = hfs_u_to_mtime(ntohl(dates[2])); - if (entry->backup_date != tmp) { - entry->backup_date = tmp; - hfs_cat_mark_dirty(entry); - } - } else { - tmp = hfs_h_to_mtime(dates[0]); - if (entry->create_date != tmp) { - entry->create_date = tmp; - hfs_cat_mark_dirty(entry); - } - tmp = hfs_h_to_mtime(dates[1]); - if (entry->modify_date != tmp) { - entry->modify_date = tmp; - inode->i_ctime = inode->i_atime = inode->i_mtime = - hfs_h_to_utime(dates[1]); - hfs_cat_mark_dirty(entry); - } - tmp = hfs_h_to_mtime(dates[2]); - if (entry->backup_date != tmp) { - entry->backup_date = tmp; - hfs_cat_mark_dirty(entry); - } + + tmp = hfs_h_to_mtime(dates[0]); + if (entry->create_date != tmp) { + entry->create_date = tmp; + hfs_cat_mark_dirty(entry); + } + tmp = hfs_h_to_mtime(dates[1]); + if (entry->modify_date != tmp) { + entry->modify_date = tmp; + inode->i_ctime = inode->i_atime = inode->i_mtime = + hfs_h_to_utime(dates[1]); + hfs_cat_mark_dirty(entry); + } + tmp = hfs_h_to_mtime(dates[2]); + if (entry->backup_date != tmp) { + entry->backup_date = tmp; + hfs_cat_mark_dirty(entry); } } diff --git a/fs/hfs/sysdep.c b/fs/hfs/sysdep.c index 0524808f7..fb68e9ab2 100644 --- a/fs/hfs/sysdep.c +++ b/fs/hfs/sysdep.c @@ -18,7 +18,7 @@ #include <linux/hfs_fs_i.h> #include <linux/hfs_fs.h> -static int hfs_revalidate_dentry(struct dentry *); +static int hfs_revalidate_dentry(struct dentry *, int); static int hfs_hash_dentry(struct dentry *, struct qstr *); static int hfs_compare_dentry(struct dentry *, struct qstr *, struct qstr *); static void hfs_dentry_iput(struct dentry *, struct inode *); @@ -89,7 +89,7 @@ static void hfs_dentry_iput(struct dentry *dentry, struct inode *inode) iput(inode); } -static int hfs_revalidate_dentry(struct dentry *dentry) +static int hfs_revalidate_dentry(struct dentry *dentry, int flags) { struct inode *inode = dentry->d_inode; int diff; diff --git a/fs/hpfs/hpfs_fs.c b/fs/hpfs/hpfs_fs.c index feafa2861..cb1d0215b 100644 --- a/fs/hpfs/hpfs_fs.c +++ b/fs/hpfs/hpfs_fs.c @@ -192,7 +192,7 @@ static ssize_t hpfs_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos); static int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir); -static int hpfs_lookup(struct inode *, struct dentry *); +static struct dentry *hpfs_lookup(struct inode *, struct dentry *); static const struct file_operations hpfs_dir_ops = { @@ -1119,7 +1119,7 @@ static secno bplus_lookup(struct inode *inode, struct bplus_header *b, * the boondocks.) */ -static int hpfs_lookup(struct inode *dir, struct dentry *dentry) +static struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry) { const char *name = dentry->d_name.name; int len = dentry->d_name.len; @@ -1129,14 +1129,6 @@ static int hpfs_lookup(struct inode *dir, struct dentry *dentry) int retval; struct quad_buffer_head qbh; - /* In case of madness */ - - retval = -ENOTDIR; - if (dir == 0) - goto out; - if (!S_ISDIR(dir->i_mode)) - goto out; - /* * Read in the directory entry. "." is there under the name ^A^A . * Always read the dir even for . and .. in case we need the dates. @@ -1208,7 +1200,7 @@ static int hpfs_lookup(struct inode *dir, struct dentry *dentry) brelse4(&qbh); out: - return retval; + return ERR_PTR(retval); } /* @@ -1374,11 +1366,6 @@ static int hpfs_readdir(struct file *filp, void * dirent, long old_pos; struct inode *inode = filp->f_dentry->d_inode; - if (inode == 0 - || inode->i_sb == 0 - || !S_ISDIR(inode->i_mode)) - return -EBADF; - tempname = (char *) __get_free_page(GFP_KERNEL); if (!tempname) return -ENOMEM; diff --git a/fs/inode.c b/fs/inode.c index 72a23f858..ac1ef535d 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -62,9 +62,8 @@ spinlock_t inode_lock = SPIN_LOCK_UNLOCKED; struct { int nr_inodes; int nr_free_inodes; - int preshrink; /* pre-shrink dcache? */ - int dummy[4]; -} inodes_stat = {0, 0, 0,}; + int dummy[5]; +} inodes_stat = {0, 0,}; int max_inodes; @@ -196,6 +195,19 @@ void sync_inodes(kdev_t dev) } /* + * Called with the spinlock already held.. + */ +static void sync_all_inodes(void) +{ + struct super_block * sb = sb_entry(super_blocks.next); + for (; sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.next)) { + if (!sb->s_dev) + continue; + sync_list(&sb->s_dirty); + } +} + +/* * Needed by knfsd */ void write_inode_now(struct inode *inode) @@ -232,13 +244,15 @@ void clear_inode(struct inode *inode) /* * Dispose-list gets a local list, so it doesn't need to - * worry about list corruption. + * worry about list corruption. It releases the inode lock + * while clearing the inodes. */ static void dispose_list(struct list_head * head) { struct list_head *next; int count = 0; + spin_unlock(&inode_lock); next = head->next; for (;;) { struct list_head * tmp = next; @@ -256,7 +270,6 @@ static void dispose_list(struct list_head * head) spin_lock(&inode_lock); list_splice(head, &inode_unused); inodes_stat.nr_free_inodes += count; - spin_unlock(&inode_lock); } /* @@ -305,65 +318,51 @@ int invalidate_inodes(struct super_block * sb) spin_lock(&inode_lock); busy = invalidate_list(&inode_in_use, sb, &throw_away); busy |= invalidate_list(&sb->s_dirty, sb, &throw_away); - spin_unlock(&inode_lock); - dispose_list(&throw_away); + spin_unlock(&inode_lock); return busy; } /* * This is called with the inode lock held. It searches - * the in-use for the specified number of freeable inodes. - * Freeable inodes are moved to a temporary list and then - * placed on the unused list by dispose_list. + * the in-use for freeable inodes, which are moved to a + * temporary list and then placed on the unused list by + * dispose_list. + * + * We don't expect to have to call this very often. * - * Note that we do not expect to have to search very hard: - * the freeable inodes will be at the old end of the list. - * - * N.B. The spinlock is released to call dispose_list. + * N.B. The spinlock is released during the call to + * dispose_list. */ #define CAN_UNUSE(inode) \ - (((inode)->i_count == 0) && \ - (!(inode)->i_state)) + (((inode)->i_count | (inode)->i_state) == 0) +#define INODE(entry) (list_entry(entry, struct inode, i_list)) -static int free_inodes(int goal) +static int free_inodes(void) { - struct list_head *tmp, *head = &inode_in_use; - LIST_HEAD(freeable); - int found = 0, depth = goal << 1; + struct list_head list, *entry, *freeable = &list; + int found = 0; + + INIT_LIST_HEAD(freeable); + entry = inode_in_use.next; + while (entry != &inode_in_use) { + struct list_head *tmp = entry; - while ((tmp = head->prev) != head && depth--) { - struct inode * inode = list_entry(tmp, struct inode, i_list); + entry = entry->next; + if (!CAN_UNUSE(INODE(tmp))) + continue; list_del(tmp); - if (CAN_UNUSE(inode)) { - list_del(&inode->i_hash); - INIT_LIST_HEAD(&inode->i_hash); - list_add(tmp, &freeable); - if (++found < goal) - continue; - break; - } - list_add(tmp, head); + list_del(&INODE(tmp)->i_hash); + INIT_LIST_HEAD(&INODE(tmp)->i_hash); + list_add(tmp, freeable); + found = 1; } - if (found) { - spin_unlock(&inode_lock); - dispose_list(&freeable); - spin_lock(&inode_lock); - } - return found; -} -static void shrink_dentry_inodes(int goal) -{ - int found; + if (found) + dispose_list(freeable); - spin_unlock(&inode_lock); - found = select_dcache(goal, 0); - if (found < goal) - found = goal; - prune_dcache(found); - spin_lock(&inode_lock); + return found; } /* @@ -373,9 +372,23 @@ static void shrink_dentry_inodes(int goal) */ static void try_to_free_inodes(int goal) { - shrink_dentry_inodes(goal); - if (!free_inodes(goal)) - shrink_dentry_inodes(goal); + /* + * First stry to just get rid of unused inodes. + * + * If we can't reach our goal that way, we'll have + * to try to shrink the dcache and sync existing + * inodes.. + */ + free_inodes(); + goal -= inodes_stat.nr_free_inodes; + if (goal > 0) { + spin_unlock(&inode_lock); + select_dcache(goal, 0); + prune_dcache(goal); + spin_lock(&inode_lock); + sync_all_inodes(); + free_inodes(); + } } /* @@ -385,17 +398,24 @@ static void try_to_free_inodes(int goal) void free_inode_memory(int goal) { spin_lock(&inode_lock); - free_inodes(goal); + free_inodes(); spin_unlock(&inode_lock); } -#define INODES_PER_PAGE PAGE_SIZE/sizeof(struct inode) + /* * This is called with the spinlock held, but releases * the lock when freeing or allocating inodes. * Look out! This returns with the inode lock held if * it got an inode.. + * + * We do inode allocations two pages at a time to reduce + * fragmentation. */ +#define INODE_PAGE_ORDER 1 +#define INODE_ALLOCATION_SIZE (PAGE_SIZE << INODE_PAGE_ORDER) +#define INODES_PER_ALLOCATION (INODE_ALLOCATION_SIZE/sizeof(struct inode)) + static struct inode * grow_inodes(void) { struct inode * inode; @@ -403,9 +423,9 @@ static struct inode * grow_inodes(void) /* * Check whether to restock the unused list. */ - if (inodes_stat.preshrink) { + if (inodes_stat.nr_inodes > max_inodes) { struct list_head *tmp; - try_to_free_inodes(8); + try_to_free_inodes(inodes_stat.nr_inodes >> 2); tmp = inode_unused.next; if (tmp != &inode_unused) { inodes_stat.nr_free_inodes--; @@ -416,14 +436,14 @@ static struct inode * grow_inodes(void) } spin_unlock(&inode_lock); - inode = (struct inode *)__get_free_page(GFP_KERNEL); + inode = (struct inode *)__get_free_pages(GFP_KERNEL,INODE_PAGE_ORDER); if (inode) { int size; struct inode * tmp; - spin_lock(&inode_lock); - size = PAGE_SIZE - 2*sizeof(struct inode); + size = INODE_ALLOCATION_SIZE - 2*sizeof(struct inode); tmp = inode; + spin_lock(&inode_lock); do { tmp++; init_once(tmp); @@ -434,11 +454,8 @@ static struct inode * grow_inodes(void) /* * Update the inode statistics */ - inodes_stat.nr_inodes += INODES_PER_PAGE; - inodes_stat.nr_free_inodes += INODES_PER_PAGE - 1; - inodes_stat.preshrink = 0; - if (inodes_stat.nr_inodes > max_inodes) - inodes_stat.preshrink = 1; + inodes_stat.nr_inodes += INODES_PER_ALLOCATION; + inodes_stat.nr_free_inodes += INODES_PER_ALLOCATION - 1; return inode; } @@ -447,10 +464,9 @@ static struct inode * grow_inodes(void) * the dcache and then try again to free some inodes. */ prune_dcache(inodes_stat.nr_inodes >> 2); - inodes_stat.preshrink = 1; spin_lock(&inode_lock); - free_inodes(inodes_stat.nr_inodes >> 2); + free_inodes(); { struct list_head *tmp = inode_unused.next; if (tmp != &inode_unused) { @@ -506,20 +522,12 @@ void clean_inode(struct inode *inode) inode->i_nlink = 1; inode->i_writecount = 0; inode->i_size = 0; + inode->i_generation = 0; memset(&inode->i_dquot, 0, sizeof(inode->i_dquot)); sema_init(&inode->i_sem, 1); } /* - * 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) -{ - sb->s_op->read_inode(inode); -} - -/* * This is called by things like the networking layer * etc that want to get an inode without any inode * number, or filesystems that allocate new inodes with @@ -588,7 +596,7 @@ add_new_inode: spin_unlock(&inode_lock); clean_inode(inode); - read_inode(inode, sb); + sb->s_op->read_inode(inode); /* * This is special! We do not need the spinlock @@ -741,7 +749,7 @@ int bmap(struct inode * inode, int block) * Initialize the hash tables and default * value for max inodes */ -#define MAX_INODE (8192) +#define MAX_INODE (16384) void __init inode_init(void) { diff --git a/fs/ioctl.c b/fs/ioctl.c index 9ac7c3bf2..8d18e453a 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -74,14 +74,20 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) filp->f_flags &= ~flag; break; - case FIOASYNC: /* O_SYNC is not yet implemented, - but it's here for completeness. */ + case FIOASYNC: if ((error = get_user(on, (int *)arg)) != 0) break; + flag = on ? FASYNC : 0; + + /* Did FASYNC state change ? */ + if ((flag ^ filp->f_flags) & FASYNC) { + if (filp->f_op && filp->f_op->fasync) + filp->f_op->fasync(fd, filp, on); + } if (on) - filp->f_flags |= O_SYNC; + filp->f_flags |= FASYNC; else - filp->f_flags &= ~O_SYNC; + filp->f_flags &= ~FASYNC; break; default: diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index a57f9680c..23ca159c7 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -295,9 +295,6 @@ static int isofs_readdir(struct file *filp, struct iso_directory_record * tmpde; struct inode *inode = filp->f_dentry->d_inode; - if (!inode || !S_ISDIR(inode->i_mode)) - return -EBADF; - tmpname = (char *) __get_free_page(GFP_KERNEL); if (!tmpname) return -ENOMEM; diff --git a/fs/isofs/file.c b/fs/isofs/file.c index 0a508c90b..e2b4405d9 100644 --- a/fs/isofs/file.c +++ b/fs/isofs/file.c @@ -16,7 +16,6 @@ #include <linux/stat.h> #include <linux/locks.h> #include <linux/fs.h> -#include <linux/iso_fs.h> /* * We have mostly NULLs here: the current defaults are OK for diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index 364698dc2..7d4ca4e98 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -74,7 +74,6 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino) char c; *ino = 0; - if (!dir) return NULL; if (!(block = dir->u.isofs_i.i_first_extent)) return NULL; @@ -228,7 +227,7 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino) return retval; } -int isofs_lookup(struct inode * dir, struct dentry * dentry) +struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry) { unsigned long ino; struct buffer_head * bh; @@ -237,12 +236,6 @@ int isofs_lookup(struct inode * dir, struct dentry * dentry) #ifdef DEBUG printk("lookup: %x %s\n",dir->i_ino, dentry->d_name.name); #endif - if (!dir) - return -ENOENT; - - if (!S_ISDIR(dir->i_mode)) - return -ENOENT; - dentry->d_op = dir->i_sb->s_root->d_op; bh = isofs_find_entry(dir, dentry, &ino); @@ -253,8 +246,8 @@ int isofs_lookup(struct inode * dir, struct dentry * dentry) inode = iget(dir->i_sb,ino); if (!inode) - return -EACCES; + return ERR_PTR(-EACCES); } d_add(dentry, inode); - return 0; + return NULL; } diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index d50df7eac..8df091923 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -30,6 +30,14 @@ static int nlm_stat_to_errno(u32 stat); */ static u32 nlm_cookie = 0x1234; +static inline void nlmclnt_next_cookie(struct nlm_cookie *c) +{ + memcpy(c->data, &nlm_cookie, 4); + memset(c->data+4, 0, 4); + c->len=4; + nlm_cookie++; +} + /* * Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls */ @@ -40,7 +48,7 @@ nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) struct nlm_lock *lock = &argp->lock; memset(argp, 0, sizeof(*argp)); - argp->cookie = nlm_cookie++; + nlmclnt_next_cookie(&argp->cookie); argp->state = nsm_local_state; lock->fh = *NFS_FH(fl->fl_file->f_dentry); lock->caller = system_utsname.nodename; @@ -57,7 +65,7 @@ nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) int nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock) { - call->a_args.cookie = nlm_cookie++; + nlmclnt_next_cookie(&call->a_args.cookie); call->a_args.lock = *lock; call->a_args.lock.caller = system_utsname.nodename; @@ -230,9 +238,24 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc) /* Perform the RPC call. If an error occurs, try again */ if ((status = rpc_call(clnt, proc, argp, resp, 0)) < 0) { dprintk("lockd: rpc_call returned error %d\n", -status); - if (status == -ERESTARTSYS) - return status; - nlm_rebind_host(host); + switch (status) { + case -EPROTONOSUPPORT: + status = -EINVAL; + break; + case -ECONNREFUSED: + case -ETIMEDOUT: + case -ENOTCONN: + status = -EAGAIN; + break; + case -ERESTARTSYS: + return signalled () ? -EINTR : status; + default: + break; + } + if (req->a_args.block) + nlm_rebind_host(host); + else + break; } else if (resp->status == NLM_LCK_DENIED_GRACE_PERIOD) { dprintk("lockd: server in grace period\n"); @@ -248,9 +271,18 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc) /* Back off a little and try again */ interruptible_sleep_on_timeout(&host->h_gracewait, 15*HZ); - } while (!signalled()); - return -ERESTARTSYS; + /* When the lock requested by F_SETLKW isn't available, + we will wait until the request can be satisfied. If + a signal is received during wait, we should return + -EINTR. */ + if (signalled ()) { + status = -EINTR; + break; + } + } while (1); + + return status; } /* diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 2bbc005ce..4072221af 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -134,7 +134,7 @@ nlm_lookup_host(struct svc_client *clnt, struct sockaddr_in *sin, host->h_addr.sin_port = 0; /* ouch! */ host->h_version = version; host->h_proto = proto; - host->h_authflavor = RPC_AUTH_NULL; + host->h_authflavor = RPC_AUTH_UNIX; host->h_rpcclnt = NULL; host->h_sema = MUTEX; host->h_nextrebind = jiffies + NLM_HOST_REBIND; diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index be9e0a5f3..d61db4302 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -64,6 +64,7 @@ lockd(struct svc_rqst *rqstp) { struct svc_serv *serv = rqstp->rq_server; int err = 0; + unsigned long grace_period_expire; /* Lock module and set up kernel thread */ MOD_INC_USE_COUNT; @@ -111,7 +112,7 @@ lockd(struct svc_rqst *rqstp) } #endif - nlmsvc_grace_period += jiffies; + grace_period_expire = nlmsvc_grace_period + jiffies; nlmsvc_timeout = nlm_timeout * HZ; /* diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index b1f1f5588..10c66ed4d 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -111,16 +111,25 @@ nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock, int remove) return NULL; } +static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b) +{ + if(a->len != b->len) + return 0; + if(memcmp(a->data,b->data,a->len)) + return 0; + return 1; +} + /* * Find a block with a given NLM cookie. */ static inline struct nlm_block * -nlmsvc_find_block(u32 cookie) +nlmsvc_find_block(struct nlm_cookie *cookie) { struct nlm_block *block; for (block = nlm_blocked; block; block = block->b_next) { - if (block->b_call.a_args.cookie == cookie) + if (nlm_cookie_match(&block->b_call.a_args.cookie,cookie)) break; } @@ -139,7 +148,7 @@ nlmsvc_find_block(u32 cookie) */ static inline struct nlm_block * nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, - struct nlm_lock *lock, u32 cookie) + struct nlm_lock *lock, struct nlm_cookie *cookie) { struct nlm_block *block; struct nlm_host *host; @@ -160,7 +169,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, lock->fl.fl_notify = nlmsvc_notify_blocked; if (!nlmclnt_setgrantargs(&block->b_call, lock)) goto failed_free; - block->b_call.a_args.cookie = cookie; /* see above */ + block->b_call.a_args.cookie = *cookie; /* see above */ dprintk("lockd: created block %p...\n", block); @@ -267,7 +276,7 @@ nlmsvc_traverse_blocks(struct nlm_host *host, struct nlm_file *file, int action) */ u32 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, - struct nlm_lock *lock, int wait, u32 cookie) + struct nlm_lock *lock, int wait, struct nlm_cookie *cookie) { struct file_lock *conflock; struct nlm_block *block; @@ -529,8 +538,8 @@ nlmsvc_grant_callback(struct rpc_task *task) unsigned long timeout; dprintk("lockd: GRANT_MSG RPC callback\n"); - if (!(block = nlmsvc_find_block(call->a_args.cookie))) { - dprintk("lockd: no block for cookie %x\n", call->a_args.cookie); + if (!(block = nlmsvc_find_block(&call->a_args.cookie))) { + dprintk("lockd: no block for cookie %x\n", *(u32 *)(call->a_args.cookie.data)); return; } @@ -560,7 +569,7 @@ nlmsvc_grant_callback(struct rpc_task *task) * block. */ void -nlmsvc_grant_reply(u32 cookie, u32 status) +nlmsvc_grant_reply(struct nlm_cookie *cookie, u32 status) { struct nlm_block *block; struct nlm_file *file; diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 2077752f4..0e59754f3 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -151,7 +151,7 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, /* Now try to lock the file */ resp->status = nlmsvc_lock(rqstp, file, &argp->lock, - argp->block, argp->cookie); + argp->block, &argp->cookie); dprintk("lockd: LOCK status %ld\n", ntohl(resp->status)); nlm_release_host(host); diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 0c3306d43..4cac77aec 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -52,8 +52,6 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, struct nlm_file *file; unsigned int hash; u32 nfserr; - uid_t saved_cr_uid; - struct svc_cred *cred; dprintk("lockd: nlm_file_lookup(%s/%u)\n", kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_ino); @@ -86,15 +84,10 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, * We have to make sure we have the right credential to open * the file. */ - cred = &rqstp->rq_cred; - saved_cr_uid = cred->cr_uid; - cred->cr_uid = 0; if ((nfserr = nlmsvc_ops->fopen(rqstp, fh, &file->f_file)) != 0) { dprintk("lockd: open failed (nfserr %ld)\n", ntohl(nfserr)); - cred->cr_uid = saved_cr_uid; goto out_free; } - cred->cr_uid = saved_cr_uid; file->f_next = nlm_files[hash]; nlm_files[hash] = file; @@ -134,7 +127,7 @@ nlm_delete_file(struct nlm_file *file) kfree(file); return; } - fp = &file->f_next; + fp = &f->f_next; } printk(KERN_WARNING "lockd: attempt to release unknown file!\n"); diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index 7d41c8a66..85fb7c729 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -53,28 +53,38 @@ nlmxdr_init(void) /* * XDR functions for basic NLM types */ -static inline u32 * -nlm_decode_cookie(u32 *p, u32 *c) +static inline u32 *nlm_decode_cookie(u32 *p, struct nlm_cookie *c) { unsigned int len; - if ((len = ntohl(*p++)) == 4) { - *c = ntohl(*p++); - } else if (len == 0) { /* hockeypux brain damage */ - *c = 0; - } else { + len = ntohl(*p++); + + if(len==0) + { + c->len=4; + memset(c->data, 0, 4); /* hockeypux brain damage */ + } + else if(len<=8) + { + c->len=len; + memcpy(c->data, p, len); + p+=(len+3)>>2; + } + else + { printk(KERN_NOTICE - "lockd: bad cookie size %d (should be 4)\n", len); + "lockd: bad cookie size %d (only cookies under 8 bytes are supported.)\n", len); return NULL; } return p; } static inline u32 * -nlm_encode_cookie(u32 *p, u32 c) +nlm_encode_cookie(u32 *p, struct nlm_cookie *c) { - *p++ = htonl(sizeof(c)); - *p++ = htonl(c); + *p++ = htonl(c->len); + memcpy(p, c->data, c->len); + p+=(c->len+3)>>2; return p; } @@ -168,7 +178,7 @@ nlm_encode_lock(u32 *p, struct nlm_lock *lock) static u32 * nlm_encode_testres(u32 *p, struct nlm_res *resp) { - if (!(p = nlm_encode_cookie(p, resp->cookie))) + if (!(p = nlm_encode_cookie(p, &resp->cookie))) return 0; *p++ = resp->status; @@ -308,7 +318,7 @@ nlmsvc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) int nlmsvc_encode_shareres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp) { - if (!(p = nlm_encode_cookie(p, resp->cookie))) + if (!(p = nlm_encode_cookie(p, &resp->cookie))) return 0; *p++ = resp->status; *p++ = xdr_zero; /* sequence argument */ @@ -318,7 +328,7 @@ nlmsvc_encode_shareres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp) int nlmsvc_encode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp) { - if (!(p = nlm_encode_cookie(p, resp->cookie))) + if (!(p = nlm_encode_cookie(p, &resp->cookie))) return 0; *p++ = resp->status; return xdr_ressize_check(rqstp, p); @@ -388,7 +398,7 @@ nlmclt_encode_testargs(struct rpc_rqst *req, u32 *p, nlm_args *argp) { struct nlm_lock *lock = &argp->lock; - if (!(p = nlm_encode_cookie(p, argp->cookie))) + if (!(p = nlm_encode_cookie(p, &argp->cookie))) return -EIO; *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero; if (!(p = nlm_encode_lock(p, lock))) @@ -429,7 +439,7 @@ nlmclt_encode_lockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp) { struct nlm_lock *lock = &argp->lock; - if (!(p = nlm_encode_cookie(p, argp->cookie))) + if (!(p = nlm_encode_cookie(p, &argp->cookie))) return -EIO; *p++ = argp->block? xdr_one : xdr_zero; *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero; @@ -446,7 +456,7 @@ nlmclt_encode_cancargs(struct rpc_rqst *req, u32 *p, nlm_args *argp) { struct nlm_lock *lock = &argp->lock; - if (!(p = nlm_encode_cookie(p, argp->cookie))) + if (!(p = nlm_encode_cookie(p, &argp->cookie))) return -EIO; *p++ = argp->block? xdr_one : xdr_zero; *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero; @@ -461,7 +471,7 @@ nlmclt_encode_unlockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp) { struct nlm_lock *lock = &argp->lock; - if (!(p = nlm_encode_cookie(p, argp->cookie))) + if (!(p = nlm_encode_cookie(p, &argp->cookie))) return -EIO; if (!(p = nlm_encode_lock(p, lock))) return -EIO; @@ -472,7 +482,7 @@ nlmclt_encode_unlockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp) static int nlmclt_encode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) { - if (!(p = nlm_encode_cookie(p, resp->cookie))) + if (!(p = nlm_encode_cookie(p, &resp->cookie))) return -EIO; *p++ = resp->status; req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); @@ -501,7 +511,7 @@ nlmclt_decode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) * Buffer requirements for NLM */ #define NLM_void_sz 0 -#define NLM_cookie_sz 2 +#define NLM_cookie_sz 3 /* 1 len , 2 data */ #define NLM_caller_sz 1+QUADLEN(sizeof(system_utsname.nodename)) #define NLM_netobj_sz 1+QUADLEN(XDR_MAX_NETOBJ) /* #define NLM_owner_sz 1+QUADLEN(NLM_MAXOWNER) */ diff --git a/fs/locks.c b/fs/locks.c index 38982227d..01d833eb8 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -111,7 +111,7 @@ #include <asm/uaccess.h> -#define OFFSET_MAX ((off_t)0x7fffffff) /* FIXME: move elsewhere? */ +#define OFFSET_MAX ((off_t)LONG_MAX) /* FIXME: move elsewhere? */ static int flock_make_lock(struct file *filp, struct file_lock *fl, unsigned int cmd); @@ -263,16 +263,18 @@ static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait) if (waiter->fl_notify) waiter->fl_notify(waiter); wake_up(&waiter->fl_wait); - if (wait) + if (wait) { /* Let the blocked process remove waiter from the * block list when it gets scheduled. */ + current->policy |= SCHED_YIELD; schedule(); - else + } else { /* Remove waiter from the block list, because by the * time it wakes up blocker won't exist any more. */ locks_delete_block(blocker, waiter); + } } return; } @@ -1170,7 +1172,7 @@ static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait) static char *lock_get_status(struct file_lock *fl, int id, char *pfx) { - static char temp[129]; + static char temp[155]; char *p = temp; struct inode *inode; diff --git a/fs/minix/dir.c b/fs/minix/dir.c index d7db754ff..187925903 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -68,8 +68,6 @@ static int minix_readdir(struct file * filp, struct minix_sb_info * info; struct inode *inode = filp->f_dentry->d_inode; - if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) - return -EBADF; info = &inode->i_sb->u.minix_sb; if (filp->f_pos & (info->s_dirsize - 1)) return -EBADF; diff --git a/fs/minix/inode.c b/fs/minix/inode.c index e8b1e7660..60032292c 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -38,14 +38,13 @@ static void minix_delete_inode(struct inode *inode) minix_free_inode(inode); } -static void minix_commit_super (struct super_block * sb, - struct minix_super_block * ms) +static void minix_commit_super(struct super_block * sb) { mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1); sb->s_dirt = 0; } -static void minix_write_super (struct super_block * sb) +static void minix_write_super(struct super_block * sb) { struct minix_super_block * ms; @@ -54,7 +53,7 @@ static void minix_write_super (struct super_block * sb) if (ms->s_state & MINIX_VALID_FS) ms->s_state &= ~MINIX_VALID_FS; - minix_commit_super (sb, ms); + minix_commit_super(sb); } sb->s_dirt = 0; } @@ -106,7 +105,7 @@ static int minix_remount (struct super_block * sb, int * flags, char * data) ms->s_state = sb->u.minix_sb.s_mount_state; mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1); sb->s_dirt = 1; - minix_commit_super (sb, ms); + minix_commit_super(sb); } else { /* Mount a partition which is read-only, read-write. */ @@ -195,8 +194,8 @@ static struct super_block *minix_read_super(struct super_block *s, void *data, s->u.minix_sb.s_ms = ms; s->u.minix_sb.s_sbh = bh; s->u.minix_sb.s_mount_state = ms->s_state; - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; + s->s_blocksize = BLOCK_SIZE; + s->s_blocksize_bits = BLOCK_SIZE_BITS; s->u.minix_sb.s_ninodes = ms->s_ninodes; s->u.minix_sb.s_nzones = ms->s_nzones; s->u.minix_sb.s_imap_blocks = ms->s_imap_blocks; diff --git a/fs/minix/namei.c b/fs/minix/namei.c index 86f99c56d..e6d680ecf 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -45,7 +45,7 @@ static struct buffer_head * minix_find_entry(struct inode * dir, struct minix_dir_entry *de; *res_dir = NULL; - if (!dir || !dir->i_sb) + if (!dir->i_sb) return NULL; info = &dir->i_sb->u.minix_sb; if (namelen > info->s_namelen) { @@ -116,7 +116,7 @@ struct dentry_operations minix_dentry_operations = { 0 /* compare */ }; -int minix_lookup(struct inode * dir, struct dentry *dentry) +struct dentry *minix_lookup(struct inode * dir, struct dentry *dentry) { struct inode * inode = NULL; struct minix_dir_entry * de; @@ -132,10 +132,10 @@ int minix_lookup(struct inode * dir, struct dentry *dentry) inode = iget(dir->i_sb, ino); if (!inode) - return -EACCES; + return ERR_PTR(-EACCES); } d_add(dentry, inode); - return 0; + return NULL; } /* @@ -221,8 +221,6 @@ int minix_create(struct inode * dir, struct dentry *dentry, int mode) struct buffer_head * bh; struct minix_dir_entry * de; - if (!dir) - return -ENOENT; inode = minix_new_inode(dir); if (!inode) return -ENOSPC; @@ -251,14 +249,6 @@ int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int rdev) struct buffer_head * bh; struct minix_dir_entry * de; - if (!dir) - return -ENOENT; - bh = minix_find_entry(dir, dentry->d_name.name, - dentry->d_name.len, &de); - if (bh) { - brelse(bh); - return -EEXIST; - } inode = minix_new_inode(dir); if (!inode) return -ENOSPC; @@ -298,15 +288,7 @@ int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode) struct minix_dir_entry * de; struct minix_sb_info * info; - if (!dir || !dir->i_sb) - return -EINVAL; info = &dir->i_sb->u.minix_sb; - bh = minix_find_entry(dir, dentry->d_name.name, - dentry->d_name.len, &de); - if (bh) { - brelse(bh); - return -EEXIST; - } if (dir->i_nlink >= info->s_link_max) return -EMLINK; inode = minix_new_inode(dir); @@ -530,15 +512,6 @@ int minix_symlink(struct inode * dir, struct dentry *dentry, brelse(name_block); inode->i_size = i; mark_inode_dirty(inode); - bh = minix_find_entry(dir, dentry->d_name.name, - dentry->d_name.len, &de); - if (bh) { - inode->i_nlink--; - mark_inode_dirty(inode); - iput(inode); - brelse(bh); - return -EEXIST; - } i = minix_add_entry(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de); if (i) { @@ -568,12 +541,6 @@ int minix_link(struct dentry * old_dentry, struct inode * dir, if (inode->i_nlink >= inode->i_sb->u.minix_sb.s_link_max) return -EMLINK; - bh = minix_find_entry(dir, dentry->d_name.name, - dentry->d_name.len, &de); - if (bh) { - brelse(bh); - return -EEXIST; - } error = minix_add_entry(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de); if (error) { @@ -604,7 +571,7 @@ int minix_link(struct dentry * old_dentry, struct inode * dir, * 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, struct dentry *old_dentry, +int minix_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir, struct dentry *new_dentry) { struct inode * old_inode, * new_inode; @@ -640,24 +607,11 @@ start_up: new_bh = NULL; } } - if (new_inode == old_inode) { - retval = 0; - goto end_rename; - } if (S_ISDIR(old_inode->i_mode)) { - retval = -EINVAL; - if (is_subdir(new_dentry, old_dentry)) - goto end_rename; if (new_inode) { - /* Prune any children before testing for busy */ - if (new_dentry->d_count > 1) - shrink_dcache_parent(new_dentry); - retval = -EBUSY; - if (new_dentry->d_count > 1) retval = -ENOTEMPTY; if (!empty_dir(new_inode)) goto end_rename; - retval = -EBUSY; } retval = -EIO; dir_bh = minix_bread(old_inode,0,0); @@ -713,8 +667,6 @@ start_up: mark_inode_dirty(new_dir); } } - /* Update the dcache */ - d_move(old_dentry, new_dentry); retval = 0; end_rename: brelse(dir_bh); @@ -722,29 +674,3 @@ end_rename: brelse(new_bh); return retval; } - -/* - * Ok, rename also locks out other renames, as they can change the parent of - * a directory, and we don't want any races. Other races are checked for by - * "do_rename()", which restarts if there are inconsistencies. - * - * Note that there is no race between different filesystems: it's only within - * 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, struct dentry *old_dentry, - struct inode * new_dir, struct dentry *new_dentry) -{ - static struct wait_queue * wait = NULL; - static int lock = 0; - int result; - - while (lock) - sleep_on(&wait); - lock = 1; - result = do_minix_rename(old_dir, old_dentry, - new_dir, new_dentry); - lock = 0; - wake_up(&wait); - return result; -} diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 055d700d9..dc9faa9a1 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -250,7 +250,7 @@ out_fail: /***** Get inode using directory and name */ -int msdos_lookup(struct inode *dir,struct dentry *dentry) +struct dentry *msdos_lookup(struct inode *dir,struct dentry *dentry) { struct super_block *sb = dir->i_sb; struct inode *inode = NULL; @@ -292,7 +292,7 @@ add: d_add(dentry, inode); res = 0; out: - return res; + return ERR_PTR(res); } @@ -327,6 +327,7 @@ static int msdos_create_entry(struct inode *dir, const char *name, fat_date_unix2dos(dir->i_mtime,&de->time,&de->date); de->size = 0; fat_mark_buffer_dirty(sb, bh, 1); + if ((*result = iget(dir->i_sb,ino)) != NULL) msdos_read_inode(*result); fat_brelse(sb, bh); @@ -405,23 +406,22 @@ static int msdos_empty(struct inode *dir) struct msdos_dir_entry *de; int result = 0; - if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */ - pos = 0; - bh = NULL; - while (fat_get_entry(dir,&pos,&bh,&de) > -1) { - /* Ignore vfat longname entries */ - if (de->attr == ATTR_EXT) - continue; - if (!IS_FREE(de->name) && - strncmp(de->name,MSDOS_DOT , MSDOS_NAME) && - strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) { - result = -ENOTEMPTY; - break; - } + pos = 0; + bh = NULL; + while (fat_get_entry(dir,&pos,&bh,&de) > -1) { + /* Ignore vfat longname entries */ + if (de->attr == ATTR_EXT) + continue; + if (!IS_FREE(de->name) && + strncmp(de->name,MSDOS_DOT , MSDOS_NAME) && + strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) { + result = -ENOTEMPTY; + break; } - if (bh) - fat_brelse(dir->i_sb, bh); } + if (bh) + fat_brelse(dir->i_sb, bh); + return result; } @@ -444,13 +444,8 @@ int msdos_rmdir(struct inode *dir, struct dentry *dentry) * whether it is empty. */ res = -EBUSY; - if (!list_empty(&dentry->d_hash)) { -#ifdef MSDOS_DEBUG -printk("msdos_rmdir: %s/%s busy, d_count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); -#endif + if (!list_empty(&dentry->d_hash)) goto rmdir_done; - } res = msdos_empty(inode); if (res) goto rmdir_done; @@ -503,14 +498,6 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode) dir->i_nlink++; inode->i_nlink = 2; /* no need to mark them dirty */ -#ifdef whatfor - /* - * He's dead, Jim. We don't d_instantiate anymore. Should do it - * from the very beginning, actually. - */ - MSDOS_I(inode)->i_busy = 1; /* prevent lookups */ -#endif - if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error; if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0) @@ -529,9 +516,6 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode) MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart; dot->i_nlink = dir->i_nlink; mark_inode_dirty(dot); -#ifdef whatfor - MSDOS_I(inode)->i_busy = 0; -#endif iput(dot); d_instantiate(dentry, inode); res = 0; @@ -606,110 +590,9 @@ int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry) return msdos_unlinkx (dir,dentry,0); } -#define MSDOS_CHECK_BUSY 1 - -/***** Rename within a directory */ -static int msdos_rename_same(struct inode *old_dir,char *old_name, - struct dentry *old_dentry, - struct inode *new_dir,char *new_name,struct dentry *new_dentry, - struct buffer_head *old_bh, - struct msdos_dir_entry *old_de, int old_ino, int is_hid) -{ - struct super_block *sb = old_dir->i_sb; - struct buffer_head *new_bh; - struct msdos_dir_entry *new_de; - struct inode *new_inode,*old_inode; - int new_ino, exists, error; - - if (!strncmp(old_name, new_name, MSDOS_NAME)) - goto set_hid; - error = -ENOENT; - if (*(unsigned char *) old_de->name == DELETED_FLAG) - goto out; - - exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0; - if (exists) { - error = -EIO; - new_inode = new_dentry->d_inode; - /* Make sure it really exists ... */ - if (!new_inode) { - printk(KERN_ERR - "msdos_rename_same: %s/%s inode NULL, ino=%d\n", - new_dentry->d_parent->d_name.name, - new_dentry->d_name.name, new_ino); - d_drop(new_dentry); - goto out_error; - } - error = S_ISDIR(new_inode->i_mode) - ? (old_de->attr & ATTR_DIR) - ? msdos_empty(new_inode) - : -EPERM - : (old_de->attr & ATTR_DIR) - ? -EPERM - : 0; - if (error) - goto out_error; - error = -EPERM; - if ((old_de->attr & ATTR_SYS)) - goto out_error; - - if (S_ISDIR(new_inode->i_mode)) { - /* make sure it's empty */ - error = msdos_empty(new_inode); - if (error) - goto out_error; -#ifdef MSDOS_CHECK_BUSY - /* check for a busy dentry */ - error = -EBUSY; - shrink_dcache_parent(new_dentry); - if (new_dentry->d_count > 1) { -printk("msdos_rename_same: %s/%s busy, count=%d\n", -new_dentry->d_parent->d_name.name, new_dentry->d_name.name, -new_dentry->d_count); - goto out_error; - } -#endif - new_dir->i_nlink--; - mark_inode_dirty(new_dir); - } - new_inode->i_nlink = 0; - MSDOS_I(new_inode)->i_busy = 1; - mark_inode_dirty(new_inode); - /* - * Make it negative if it's not busy; - * otherwise let d_move() drop it. - */ - if (new_dentry->d_count == 1) - d_delete(new_dentry); - - new_de->name[0] = DELETED_FLAG; - fat_mark_buffer_dirty(sb, new_bh, 1); - fat_brelse(sb, new_bh); - } - memcpy(old_de->name, new_name, MSDOS_NAME); - /* Update the dcache */ - d_move(old_dentry, new_dentry); -set_hid: - old_de->attr = is_hid - ? (old_de->attr | ATTR_HIDDEN) - : (old_de->attr &~ ATTR_HIDDEN); - fat_mark_buffer_dirty(sb, old_bh, 1); - /* update binary info for conversion, i_attrs */ - old_inode = old_dentry->d_inode; - MSDOS_I(old_inode)->i_attrs = is_hid - ? (MSDOS_I(old_inode)->i_attrs | ATTR_HIDDEN) - : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN); - error = 0; -out: - return error; - -out_error: - fat_brelse(sb, new_bh); - goto out; -} - +/* Now we could merge it with msdos_rename_same. Later */ /***** Rename across directories - a nonphysical move */ -static int msdos_rename_diff(struct inode *old_dir, char *old_name, +static int do_msdos_rename(struct inode *old_dir, char *old_name, struct dentry *old_dentry, struct inode *new_dir,char *new_name, struct dentry *new_dentry, struct buffer_head *old_bh, @@ -718,30 +601,27 @@ static int msdos_rename_diff(struct inode *old_dir, char *old_name, struct super_block *sb = old_dir->i_sb; struct buffer_head *new_bh,*free_bh,*dotdot_bh; struct msdos_dir_entry *new_de,*free_de,*dotdot_de; - struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode; + struct inode *old_inode,*new_inode,*dotdot_inode; int new_ino,free_ino,dotdot_ino; int error, exists; - error = -EINVAL; - if (old_ino == new_dir->i_ino) - goto out; - /* prevent moving directory below itself */ - if (is_subdir(new_dentry, old_dentry)) - goto out; - + old_inode = old_dentry->d_inode; + if (old_dir==new_dir && !strncmp(old_name, new_name, MSDOS_NAME)) + goto set_hid; error = -ENOENT; if (*(unsigned char *) old_de->name == DELETED_FLAG) goto out; /* find free spot */ - while ((error = fat_scan(new_dir, NULL, &free_bh, &free_de, &free_ino, - SCAN_ANY)) < 0) { - if (error != -ENOENT) - goto out; - error = fat_add_cluster(new_dir); - if (error) - goto out; - } + if (new_dir!=old_dir) + while ((error = fat_scan(new_dir, NULL, &free_bh, &free_de, + &free_ino, SCAN_ANY)) < 0) { + if (error != -ENOENT) + goto out; + error = fat_add_cluster(new_dir); + if (error) + goto out; + } exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0; if (exists) { /* Trash the old file! */ @@ -750,40 +630,17 @@ static int msdos_rename_diff(struct inode *old_dir, char *old_name, /* Make sure it really exists ... */ if (!new_inode) { printk(KERN_ERR - "msdos_rename_diff: %s/%s inode NULL, ino=%d\n", + "msdos_rename: %s/%s inode NULL, ino=%d\n", new_dentry->d_parent->d_name.name, new_dentry->d_name.name, new_ino); d_drop(new_dentry); goto out_new; } - error = S_ISDIR(new_inode->i_mode) - ? (old_de->attr & ATTR_DIR) - ? msdos_empty(new_inode) - : -EPERM - : (old_de->attr & ATTR_DIR) - ? -EPERM - : 0; - if (error) - goto out_new; error = -EPERM; if ((old_de->attr & ATTR_SYS)) goto out_new; -#ifdef MSDOS_CHECK_BUSY - /* check for a busy dentry */ - error = -EBUSY; - if (new_dentry->d_count > 1) { - shrink_dcache_parent(new_dentry); - if (new_dentry->d_count > 1) { -printk("msdos_rename_diff: target %s/%s busy, count=%d\n", -new_dentry->d_parent->d_name.name, new_dentry->d_name.name, -new_dentry->d_count); - goto out_new; - } - } -#endif if (S_ISDIR(new_inode->i_mode)) { - /* make sure it's empty */ error = msdos_empty(new_inode); if (error) goto out_new; @@ -793,18 +650,26 @@ new_dentry->d_count); new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; mark_inode_dirty(new_inode); - /* - * Make it negative if it's not busy; - * otherwise let d_move() drop it. - */ - if (new_dentry->d_count == 1) - d_delete(new_dentry); + new_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, new_bh, 1); fat_brelse(sb, new_bh); } - old_inode = old_dentry->d_inode; + if (old_dir==new_dir) { + memcpy(old_de->name, new_name, MSDOS_NAME); +set_hid: + old_de->attr = is_hid + ? (old_de->attr | ATTR_HIDDEN) + : (old_de->attr &~ ATTR_HIDDEN); + fat_mark_buffer_dirty(sb, old_bh, 1); + MSDOS_I(old_inode)->i_attrs = is_hid + ? (MSDOS_I(old_inode)->i_attrs | ATTR_HIDDEN) + : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN); + error = 0; + goto out; + } + /* Get the dotdot inode if we'll need it ... */ dotdot_bh = NULL; dotdot_inode = NULL; @@ -824,73 +689,37 @@ new_dentry->d_count); goto out_dotdot; } - /* get an inode for the new name */ + /* + * Potential race here. It will go away when we'll switch to + * sane inumbers (along with a frigging lot of other races). + */ + + /* set new entry */ memcpy(free_de, old_de, sizeof(struct msdos_dir_entry)); memcpy(free_de->name, new_name, MSDOS_NAME); free_de->attr = is_hid ? (free_de->attr|ATTR_HIDDEN) : (free_de->attr&~ATTR_HIDDEN); - error = -EIO; - free_inode = iget(sb, free_ino); - if (!free_inode) - goto out_iput; - /* make sure it's not busy! */ - if (MSDOS_I(free_inode)->i_busy) - printk(KERN_ERR "msdos_rename_diff: new inode %ld busy!\n", - (ino_t) free_ino); - if (!list_empty(&free_inode->i_dentry)) - printk("msdos_rename_diff: free inode has aliases??\n"); - msdos_read_inode(free_inode); - - /* - * Make sure the old dentry isn't busy, - * as we need to change inodes ... - */ - error = -EBUSY; - if (old_dentry->d_count > 1) { - shrink_dcache_parent(old_dentry); - if (old_dentry->d_count > 1) { -printk("msdos_rename_diff: source %s/%s busy, count=%d\n", -old_dentry->d_parent->d_name.name, old_dentry->d_name.name, -old_dentry->d_count); - goto out_iput; - } - } - - /* keep the inode for a bit ... */ - old_inode->i_count++; - d_delete(old_dentry); - - free_inode->i_mode = old_inode->i_mode; - free_inode->i_nlink = old_inode->i_nlink; - free_inode->i_size = old_inode->i_size; - free_inode->i_blocks = old_inode->i_blocks; - free_inode->i_mtime = old_inode->i_mtime; - free_inode->i_atime = old_inode->i_atime; - free_inode->i_ctime = old_inode->i_ctime; - MSDOS_I(free_inode)->i_ctime_ms = MSDOS_I(old_inode)->i_ctime_ms; - - MSDOS_I(free_inode)->i_start = MSDOS_I(old_inode)->i_start; - MSDOS_I(free_inode)->i_logstart = MSDOS_I(old_inode)->i_logstart; - MSDOS_I(free_inode)->i_attrs = MSDOS_I(old_inode)->i_attrs; - - /* release the old inode's resources */ - MSDOS_I(old_inode)->i_start = 0; - MSDOS_I(old_inode)->i_logstart = 0; - old_inode->i_nlink = 0; - /* - * Install the new inode ... + * Now the tricky part. We need to change i_ino. icache ignores + * i_ino for unhashed inodes, so we'll remove inode from hash, + * change what we want to change and reinsert it back. NB: we + * don't have to invalidate FAT cache here - all we need is to + * flip i_ino in relevant cache entries. Later. */ - d_instantiate(old_dentry, free_inode); + remove_inode_hash(old_inode); - fat_mark_buffer_dirty(sb, free_bh, 1); fat_cache_inval_inode(old_inode); - mark_inode_dirty(old_inode); + old_inode->i_version = ++event; + MSDOS_I(old_inode)->i_binary = + fat_is_binary(MSDOS_SB(sb)->options.conversion, free_de->ext); + old_inode->i_ino = free_ino; + fat_mark_buffer_dirty(sb, free_bh, 1); old_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, old_bh, 1); - iput(old_inode); + + insert_inode_hash(old_inode); /* a directory? */ if (dotdot_bh) { @@ -908,8 +737,6 @@ old_dentry->d_count); fat_brelse(sb, dotdot_bh); } - /* Update the dcache */ - d_move(old_dentry, new_dentry); error = 0; rename_done: @@ -917,14 +744,6 @@ rename_done: out: return error; -out_iput: - free_de->name[0] = DELETED_FLAG; - /* - * Don't mark free_bh as dirty. Both states - * are supposed to be equivalent. - */ - iput(free_inode); /* may be NULL */ - iput(dotdot_inode); out_dotdot: fat_brelse(sb, dotdot_bh); goto rename_done; @@ -944,9 +763,6 @@ int msdos_rename(struct inode *old_dir,struct dentry *old_dentry, int is_hid,old_hid; /* if new file and old file are hidden */ char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME]; - error = -EINVAL; - if (sb != new_dir->i_sb) - goto rename_done; error = msdos_format_name(MSDOS_SB(sb)->options.name_check, old_dentry->d_name.name, old_dentry->d_name.len, old_msdos_name, 1,MSDOS_SB(sb)->options.dotsOK); @@ -966,14 +782,9 @@ int msdos_rename(struct inode *old_dir,struct dentry *old_dentry, goto rename_done; fat_lock_creation(); - if (old_dir == new_dir) - error = msdos_rename_same(old_dir, old_msdos_name, old_dentry, - new_dir, new_msdos_name, new_dentry, - old_bh, old_de, (ino_t)old_ino, is_hid); - else - error = msdos_rename_diff(old_dir, old_msdos_name, old_dentry, - new_dir, new_msdos_name, new_dentry, - old_bh, old_de, (ino_t)old_ino, is_hid); + error = do_msdos_rename(old_dir, old_msdos_name, old_dentry, + new_dir, new_msdos_name, new_dentry, + old_bh, old_de, (ino_t)old_ino, is_hid); fat_unlock_creation(); fat_brelse(sb, old_bh); diff --git a/fs/namei.c b/fs/namei.c index d89d6d64c..b91b43a1f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -23,18 +23,6 @@ #include <asm/page.h> #include <asm/pgtable.h> -/* - * The bitmask for a lookup event: - * - follow links at the end - * - require a directory - * - ending slashes ok even for nonexistent files - * - internal "there are more path compnents" flag - */ -#define LOOKUP_FOLLOW (1) -#define LOOKUP_DIRECTORY (2) -#define LOOKUP_SLASHOK (4) -#define LOOKUP_CONTINUE (8) - #include <asm/namei.h> /* This can be removed after the beta phase. */ @@ -225,12 +213,12 @@ static struct dentry * reserved_lookup(struct dentry * parent, struct qstr * nam /* * Internal lookup() using the new generic dcache. */ -static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name) +static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, int flags) { struct dentry * dentry = d_lookup(parent, name); if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { - if (!dentry->d_op->d_revalidate(dentry) && !d_invalidate(dentry)) { + if (!dentry->d_op->d_revalidate(dentry, flags) && !d_invalidate(dentry)) { dput(dentry); dentry = NULL; } @@ -245,7 +233,7 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name) * 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) +static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, int flags) { struct dentry * result; struct inode *dir = parent->d_inode; @@ -258,17 +246,16 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name) * FIXME! This could use version numbering or similar to * avoid unnecessary cache lookups. */ - result = cached_lookup(parent, name); + result = cached_lookup(parent, name, flags); if (!result) { struct dentry * dentry = d_alloc(parent, name); result = ERR_PTR(-ENOMEM); if (dentry) { - int error = dir->i_op->lookup(dir, dentry); - result = dentry; - if (error) { + result = dir->i_op->lookup(dir, dentry); + if (result) dput(dentry); - result = ERR_PTR(error); - } + else + result = dentry; } } up(&dir->i_sem); @@ -279,7 +266,8 @@ static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry { struct inode * inode = dentry->d_inode; - if (inode && inode->i_op && inode->i_op->follow_link) { + if ((follow & LOOKUP_FOLLOW) + && inode && inode->i_op && inode->i_op->follow_link) { if (current->link_count < 5) { struct dentry * result; @@ -392,9 +380,9 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned /* This does the actual lookups.. */ dentry = reserved_lookup(base, &this); if (!dentry) { - dentry = cached_lookup(base, &this); + dentry = cached_lookup(base, &this, flags); if (!dentry) { - dentry = real_lookup(base, &this); + dentry = real_lookup(base, &this, flags); if (IS_ERR(dentry)) break; } @@ -403,9 +391,6 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned /* Check mountpoints.. */ dentry = follow_mount(dentry); - if (!(flags & LOOKUP_FOLLOW)) - break; - base = do_follow_link(base, dentry, flags); if (IS_ERR(base)) goto return_base; @@ -1034,17 +1019,13 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry) int error; error = may_delete(dir, dentry, 0); - if (error) - goto exit_lock; - - if (!dir->i_op || !dir->i_op->unlink) - goto exit_lock; - - DQUOT_INIT(dir); - - error = dir->i_op->unlink(dir, dentry); - -exit_lock: + if (!error) { + error = -EPERM; + if (dir->i_op && dir->i_op->unlink) { + DQUOT_INIT(dir); + error = dir->i_op->unlink(dir, dentry); + } + } return error; } @@ -1231,15 +1212,16 @@ asmlinkage int sys_link(const char * oldname, const char * newname) return error; } -int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, +int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int error; - int isdir; + int need_rehash = 0; - isdir = S_ISDIR(old_dentry->d_inode->i_mode); + if (old_dentry->d_inode == new_dentry->d_inode) + return 0; - error = may_delete(old_dir, old_dentry, isdir); /* XXX */ + error = may_delete(old_dir, old_dentry, 1); if (error) return error; @@ -1249,20 +1231,91 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (!new_dentry->d_inode) error = may_create(new_dir, new_dentry); else - error = may_delete(new_dir, new_dentry, isdir); + error = may_delete(new_dir, new_dentry, 1); if (error) return error; if (!old_dir->i_op || !old_dir->i_op->rename) return -EPERM; + /* + * If we are going to change the parent - check write permissions, + * we'll need to flip '..'. + */ + if (new_dir != old_dir) { + error = permission(old_dentry->d_inode, MAY_WRITE); + } + if (error) + return error; + DQUOT_INIT(old_dir); DQUOT_INIT(new_dir); + down(&old_dir->i_sb->s_vfs_rename_sem); + error = -EINVAL; + if (is_subdir(new_dentry, old_dentry)) + goto out_unlock; + if (new_dentry->d_inode) { + error = -EBUSY; + if (d_invalidate(new_dentry)<0) + goto out_unlock; + need_rehash = 1; + } error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); - + if (need_rehash) + d_rehash(new_dentry); + if (!error) + d_move(old_dentry,new_dentry); +out_unlock: + up(&old_dir->i_sb->s_vfs_rename_sem); return error; } +int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + int error; + + if (old_dentry->d_inode == new_dentry->d_inode) + return 0; + + error = may_delete(old_dir, old_dentry, 0); + if (error) + return error; + + if (new_dir->i_dev != old_dir->i_dev) + return -EXDEV; + + if (!new_dentry->d_inode) + error = may_create(new_dir, new_dentry); + else + error = may_delete(new_dir, new_dentry, 0); + if (error) + return error; + + if (!old_dir->i_op || !old_dir->i_op->rename) + return -EPERM; + + DQUOT_INIT(old_dir); + DQUOT_INIT(new_dir); + error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); + if (error) + return error; + /* The following d_move() should become unconditional */ + if (!(old_dir->i_sb->s_flags & MS_ODD_RENAME)) { + d_move(old_dentry, new_dentry); + } + return 0; +} + +int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + if (S_ISDIR(old_dentry->d_inode->i_mode)) + return vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); + else + return vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry); +} + static inline int do_rename(const char * oldname, const char * newname) { int error; diff --git a/fs/ncpfs/Config.in b/fs/ncpfs/Config.in index 21a7af854..11a1b1d36 100644 --- a/fs/ncpfs/Config.in +++ b/fs/ncpfs/Config.in @@ -6,5 +6,10 @@ bool ' Proprietary file locking' CONFIG_NCPFS_IOCTL_LOCKING bool ' Clear remove/delete inhibit when needed' CONFIG_NCPFS_STRONG bool ' Use NFS namespace if available' CONFIG_NCPFS_NFS_NS bool ' Use LONG (OS/2) namespace if available' CONFIG_NCPFS_OS2_NS +if [ "$CONFIG_NCPFS_OS2_NS" = "y" ]; then + bool ' Lowercase DOS filenames' CONFIG_NCPFS_SMALLDOS +fi bool ' Allow mounting of volume subdirectories' CONFIG_NCPFS_MOUNT_SUBDIR # bool ' NDS interserver authentication support' CONFIG_NCPFS_NDS_DOMAINS +bool ' Use Native Language Support' CONFIG_NCPFS_NLS +bool ' Enable symbolic links and execute flags' CONFIG_NCPFS_EXTRAS diff --git a/fs/ncpfs/Makefile b/fs/ncpfs/Makefile index 109091ef0..2fb40609c 100644 --- a/fs/ncpfs/Makefile +++ b/fs/ncpfs/Makefile @@ -9,7 +9,7 @@ O_TARGET := ncpfs.o O_OBJS := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o \ - ncpsign_kernel.o + symlink.o ncpsign_kernel.o M_OBJS := $(O_TARGET) # If you want debugging output, please uncomment the following line diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 9ef53c41a..cacc0d5c5 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -4,6 +4,7 @@ * Copyright (C) 1995, 1996 by Volker Lendecke * Modified for big endian by J.F. Chadima and David S. Miller * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache + * Modified 1998 Wolfram Pienkoss for NLS * */ @@ -22,6 +23,7 @@ #include <linux/locks.h> #include <linux/ncp_fs.h> + #include "ncplib_kernel.h" struct ncp_dirent { @@ -48,13 +50,16 @@ static ssize_t ncp_dir_read(struct file *, char *, size_t, loff_t *); static int ncp_readdir(struct file *, void *, filldir_t); static int ncp_create(struct inode *, struct dentry *, int); -static int ncp_lookup(struct inode *, struct dentry *); +static struct dentry *ncp_lookup(struct inode *, struct dentry *); static int ncp_unlink(struct inode *, struct dentry *); static int ncp_mkdir(struct inode *, struct dentry *, int); static int ncp_rmdir(struct inode *, struct dentry *); static int ncp_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); - +#ifdef CONFIG_NCPFS_EXTRAS +extern int ncp_symlink(struct inode *, struct dentry *, const char *); +#endif + static struct file_operations ncp_dir_operations = { NULL, /* lseek - default */ @@ -77,7 +82,11 @@ struct inode_operations ncp_dir_inode_operations = ncp_lookup, /* lookup */ NULL, /* link */ ncp_unlink, /* unlink */ +#ifdef CONFIG_NCPFS_EXTRAS + ncp_symlink, /* symlink */ +#else NULL, /* symlink */ +#endif ncp_mkdir, /* mkdir */ ncp_rmdir, /* rmdir */ NULL, /* mknod */ @@ -103,14 +112,14 @@ ncp_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos) /* * Dentry operations routines */ -static int ncp_lookup_validate(struct dentry *); +static int ncp_lookup_validate(struct dentry *, int); static int ncp_hash_dentry(struct dentry *, struct qstr *); static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *); static void ncp_delete_dentry(struct dentry *); struct dentry_operations ncp_dentry_operations = { - ncp_lookup_validate, /* d_validate(struct dentry *) */ + ncp_lookup_validate, /* d_revalidate(struct dentry *, int) */ ncp_hash_dentry, /* d_hash */ ncp_compare_dentry, /* d_compare */ ncp_delete_dentry /* d_delete(struct dentry *) */ @@ -191,14 +200,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name); } } -/* Here we encapsulate the inode number handling that depends upon the - * mount mode: When we mount a complete server, the memory address of - * the ncp_inode_info is used as the inode number. When only a single - * volume is mounted, then the dirEntNum is used as the inode - * number. As this is unique for the complete volume, this should - * enable the NFS exportability of a ncpfs-mounted volume. - */ - /* * Generate a unique inode number. */ @@ -253,37 +254,30 @@ static int ncp_force_unlink(struct inode *dir, struct dentry* dentry) { int res=0x9c,res2; - struct iattr ia; + struct nw_modify_dos_info info; + __u32 old_nwattr; + struct inode *inode; + memset(&info, 0, sizeof(info)); + /* remove the Read-Only flag on the NW server */ + inode = dentry->d_inode; - memset(&ia,0,sizeof(struct iattr)); - ia.ia_mode = dentry->d_inode->i_mode; - ia.ia_mode |= NCP_SERVER(dir)->m.file_mode & 0222; /* set write bits */ - ia.ia_valid = ATTR_MODE; - - res2=ncp_notify_change(dentry, &ia); - if (res2) - { - goto leave_me; - } + old_nwattr = NCP_FINFO(inode)->nwattr; + info.attributes = old_nwattr & ~(aRONLY|aDELETEINHIBIT|aRENAMEINHIBIT); + res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info); + if (res2) + goto leave_me; /* now try again the delete operation */ - res = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry); if (res) /* delete failed, set R bit again */ { - memset(&ia,0,sizeof(struct iattr)); - ia.ia_mode = dentry->d_inode->i_mode; - ia.ia_mode &= ~(NCP_SERVER(dir)->m.file_mode & 0222); /* clear write bits */ - ia.ia_valid = ATTR_MODE; - - res2=ncp_notify_change(dentry, &ia); - if (res2) - { + info.attributes = old_nwattr; + res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info); + if (res2) goto leave_me; - } } leave_me: return(res); @@ -293,68 +287,65 @@ leave_me: #ifdef CONFIG_NCPFS_STRONG static int ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name, - struct inode *new_dir, struct dentry* new_dentry, char *_new_name, - int *done_flag) + struct inode *new_dir, struct dentry* new_dentry, char *_new_name) { + struct nw_modify_dos_info info; int res=0x90,res2; - struct iattr ia; + struct inode *old_inode = old_dentry->d_inode; + __u32 old_nwattr = NCP_FINFO(old_inode)->nwattr; + __u32 new_nwattr = 0; /* shut compiler warning */ + int old_nwattr_changed = 0; + int new_nwattr_changed = 0; + memset(&info, 0, sizeof(info)); + /* remove the Read-Only flag on the NW server */ - memset(&ia,0,sizeof(struct iattr)); - ia.ia_mode = old_dentry->d_inode->i_mode; - ia.ia_mode |= NCP_SERVER(old_dir)->m.file_mode & 0222; /* set write bits */ - ia.ia_valid = ATTR_MODE; - - res2=ncp_notify_change(old_dentry, &ia); - if (res2) - { - goto leave_me; - } - + info.attributes = old_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); + res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info); + if (!res2) + old_nwattr_changed = 1; + if (new_dentry && new_dentry->d_inode) { + new_nwattr = NCP_FINFO(new_dentry->d_inode)->nwattr; + info.attributes = new_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); + res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info); + if (!res2) + new_nwattr_changed = 1; + } /* now try again the rename operation */ - res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir), - old_dir, _old_name, - new_dir, _new_name); - - if (!res) { - ncp_invalid_dir_cache(old_dir); - ncp_invalid_dir_cache(new_dir); - d_move(old_dentry,new_dentry); - *done_flag=1; - - if (!old_dentry->d_inode) { - DPRINTK(KERN_INFO "ncpfs: no inode -- file remains rw\n"); - goto leave_me; - } - if ((res2=ncp_lookup_validate(old_dentry))) { - DPRINTK(KERN_DEBUG "ncpfs: ncp_lookup_validate returned %d\n",res2); - } - } - - memset(&ia,0,sizeof(struct iattr)); - ia.ia_mode = old_dentry->d_inode->i_mode; - ia.ia_mode &= ~(NCP_SERVER(old_dentry->d_inode)->m.file_mode & 0222); /* clear write bits */ - ia.ia_valid = ATTR_MODE; - - DPRINTK(KERN_INFO "calling ncp_notify_change() with %s/%s\n", - old_dentry->d_parent->d_name.name,old_dentry->d_name.name); - - res2=ncp_notify_change(old_dentry, &ia); - if (res2) - { - printk(KERN_INFO "ncpfs: ncp_notify_change (2) failed: %08x\n",res2); - /* goto leave_me; */ - } - - leave_me: + /* but only if something really happened */ + if (new_nwattr_changed || old_nwattr_changed) { + res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir), + old_dir, _old_name, + new_dir, _new_name); + } + if (res) + goto leave_me; + /* file was successfully renamed, so: + do not set attributes on old file - it no longer exists + copy attributes from old file to new */ + new_nwattr_changed = old_nwattr_changed; + new_nwattr = old_nwattr; + old_nwattr_changed = 0; + +leave_me:; + if (old_nwattr_changed) { + info.attributes = old_nwattr; + res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info); + /* ignore errors */ + } + if (new_nwattr_changed) { + info.attributes = new_nwattr; + res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info); + /* ignore errors */ + } return(res); } #endif /* CONFIG_NCPFS_STRONG */ static int -ncp_lookup_validate(struct dentry * dentry) +ncp_lookup_validate(struct dentry * dentry, int flags) { struct ncp_server *server; struct inode *dir = dentry->d_parent->d_inode; @@ -384,11 +375,6 @@ ncp_lookup_validate(struct dentry * dentry) printk(KERN_DEBUG "ncp_lookup_validate: %s, len %d\n", __name, len); #endif - if (!ncp_preserve_case(dir)) { - str_lower(__name); - down_case = 1; - } - /* If the file is in the dir cache, we do not have to ask the server. */ @@ -398,17 +384,14 @@ dentry->d_parent->d_name.name, __name); #endif if (ncp_is_server_root(dir)) { - str_upper(__name); + io2vol(server, __name, 1); down_case = 1; res = ncp_lookup_volume(server, __name, &(finfo.nw_info.i)); } else { - if (!ncp_preserve_case(dir)) - { - str_upper(__name); - down_case = 1; - } + down_case = !ncp_preserve_case(dir); + io2vol(server, __name, down_case); res = ncp_obtain_info(server, dir, __name, &(finfo.nw_info.i)); } @@ -427,6 +410,9 @@ dentry->d_parent->d_name.name, __name, res); else printk(KERN_DEBUG "ncp_lookup_validate: found, but dirEntNum changed\n"); #endif + vol2io(server, finfo.nw_info.i.entryName, + !ncp_preserve_entry_case(dir, + finfo.nw_info.i.NSCreator)); ncp_update_inode2(dentry->d_inode, &finfo.nw_info); } if (!val) ncp_invalid_dir_cache(dir); @@ -454,11 +440,6 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir) DDPRINTK(KERN_DEBUG "ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n", inode->i_ino, c_ino); - result = -EBADF; - if (!inode || !S_ISDIR(inode->i_mode)) { - printk(KERN_WARNING "ncp_readdir: inode is NULL or not a directory\n"); - goto out; - } result = -EIO; if (!ncp_conn_valid(server)) goto out; @@ -533,10 +514,11 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir) c_last_returned_index = 0; index = 0; - if (!ncp_preserve_case(inode)) { - for (i = 0; i < c_size; i++) { - str_lower(c_entry[i].i.entryName); - } + for (i = 0; i < c_size; i++) + { + vol2io(server, c_entry[i].i.entryName, + !ncp_preserve_entry_case(inode, + c_entry[i].i.NSCreator)); } } } @@ -728,7 +710,7 @@ int ncp_conn_logged_in(struct ncp_server *server) struct dentry* dent; result = -ENOENT; - str_upper(server->m.mounted_vol); + io2vol(server, server->m.mounted_vol, 1); if (ncp_lookup_volume(server, server->m.mounted_vol, &(server->root.finfo.i)) != 0) { #ifdef NCPFS_PARANOIA @@ -736,7 +718,7 @@ printk(KERN_DEBUG "ncp_conn_logged_in: %s not found\n", server->m.mounted_vol); #endif goto out; } - str_lower(server->root.finfo.i.entryName); + vol2io(server, server->root.finfo.i.entryName, 1); dent = server->root_dentry; if (dent) { struct inode* ino = dent->d_inode; @@ -757,7 +739,7 @@ out: return result; } -static int ncp_lookup(struct inode *dir, struct dentry *dentry) +static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry) { struct ncp_server *server; struct inode *inode = NULL; @@ -767,11 +749,6 @@ static int ncp_lookup(struct inode *dir, struct dentry *dentry) struct ncpfs_inode_info finfo; __u8 __name[dentry->d_name.len + 1]; - error = -ENOENT; - if (!dir || !S_ISDIR(dir->i_mode)) { - printk(KERN_WARNING "ncp_lookup: inode is NULL or not a directory.\n"); - goto finished; - } server = NCP_SERVER(dir); error = -EIO; @@ -784,11 +761,6 @@ static int ncp_lookup(struct inode *dir, struct dentry *dentry) printk(KERN_DEBUG "ncp_lookup: %s, len %d\n", __name, len); #endif - if (!ncp_preserve_case(dir)) { - str_lower(__name); - down_case = 1; - } - /* If the file is in the dir cache, we do not have to ask the server. */ @@ -828,17 +800,14 @@ dentry->d_parent->d_name.name, __name); #endif if (ncp_is_server_root(dir)) { - str_upper(__name); + io2vol(server, __name, 1); down_case = 1; res = ncp_lookup_volume(server, __name, &(finfo.nw_info.i)); } else { - if (!ncp_preserve_case(dir)) - { - str_upper(__name); - down_case = 1; - } + down_case = !ncp_preserve_case(dir); + io2vol(server, __name, down_case); res = ncp_obtain_info(server, dir, __name, &(finfo.nw_info.i)); } @@ -849,8 +818,11 @@ dentry->d_parent->d_name.name, __name, res); /* * If we didn't find an entry, make a negative dentry. */ - if (res != 0) + if (res != 0) { goto add_entry; + } else vol2io(server, finfo.nw_info.i.entryName, + !ncp_preserve_entry_case(dir, + finfo.nw_info.i.NSCreator)); } /* @@ -872,7 +844,7 @@ finished: #ifdef NCPFS_PARANOIA printk(KERN_DEBUG "ncp_lookup: result=%d\n", error); #endif - return error; + return ERR_PTR(error); } /* @@ -904,20 +876,17 @@ dentry->d_parent->d_name.name, dentry->d_name.name); goto out; } -static int ncp_create(struct inode *dir, struct dentry *dentry, int mode) +int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode, + int attributes) { int error, result; struct ncpfs_inode_info finfo; __u8 _name[dentry->d_name.len + 1]; - + #ifdef NCPFS_PARANOIA -printk(KERN_DEBUG "ncp_create: creating %s/%s, mode=%x\n", +printk(KERN_DEBUG "ncp_create_new: creating %s/%s, mode=%x\n", dentry->d_parent->d_name.name, dentry->d_name.name, mode); #endif - if (!dir || !S_ISDIR(dir->i_mode)) { - printk(KERN_WARNING "ncp_create: inode is NULL or not a directory\n"); - return -ENOENT; - } error = -EIO; if (!ncp_conn_valid(NCP_SERVER(dir))) goto out; @@ -925,14 +894,12 @@ dentry->d_parent->d_name.name, dentry->d_name.name, mode); strncpy(_name, dentry->d_name.name, dentry->d_name.len); _name[dentry->d_name.len] = '\0'; - if (!ncp_preserve_case(dir)) { - str_upper(_name); - } + io2vol(NCP_SERVER(dir), _name, !ncp_preserve_case(dir)); error = -EACCES; result = ncp_open_create_file_or_subdir(NCP_SERVER(dir), dir, _name, OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE, - 0, AR_READ | AR_WRITE, &finfo.nw_info); + attributes, AR_READ | AR_WRITE, &finfo.nw_info); if (!result) { finfo.nw_info.access = O_RDWR; error = ncp_instantiate(dir, dentry, &finfo); @@ -946,6 +913,11 @@ out: return error; } +static int ncp_create(struct inode *dir, struct dentry *dentry, int mode) +{ + return ncp_create_new(dir, dentry, mode, 0); +} + static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int error; @@ -954,20 +926,13 @@ static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode) DPRINTK(KERN_DEBUG "ncp_mkdir: making %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); - error = -ENOTDIR; - if (!dir || !S_ISDIR(dir->i_mode)) { - printk(KERN_WARNING "ncp_mkdir: inode is NULL or not a directory\n"); - goto out; - } error = -EIO; if (!ncp_conn_valid(NCP_SERVER(dir))) goto out; strncpy(_name, dentry->d_name.name, dentry->d_name.len); _name[dentry->d_name.len] = '\0'; - if (!ncp_preserve_case(dir)) { - str_upper(_name); - } + io2vol(NCP_SERVER(dir), _name, !ncp_preserve_case(dir)); error = -EACCES; if (ncp_open_create_file_or_subdir(NCP_SERVER(dir), dir, _name, @@ -987,13 +952,6 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry) DPRINTK(KERN_DEBUG "ncp_rmdir: removing %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); - - error = -ENOENT; - if (!dir || !S_ISDIR(dir->i_mode)) - { - printk(KERN_WARNING "ncp_rmdir: inode is NULL or not a directory\n"); - goto out; - } error = -EIO; if (!ncp_conn_valid(NCP_SERVER(dir))) @@ -1006,17 +964,34 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry) strncpy(_name, dentry->d_name.name, dentry->d_name.len); _name[dentry->d_name.len] = '\0'; - if (!ncp_preserve_case(dir)) - { - str_upper(_name); - } - error = -EACCES; + io2vol(NCP_SERVER(dir), _name, !ncp_preserve_case(dir)); result = ncp_del_file_or_subdir(NCP_SERVER(dir), dir, _name); - if (!result) - { - ncp_invalid_dir_cache(dir); - error = 0; - } + switch (result) { + case 0x00: + ncp_invalid_dir_cache(dir); + error = 0; + break; + case 0x85: /* unauthorized to delete file */ + case 0x8A: /* unauthorized to delete file */ + error = -EACCES; + break; + case 0x8F: + case 0x90: /* read only */ + error = -EPERM; + break; + case 0x9F: /* in use by another client */ + error = -EBUSY; + break; + case 0xA0: /* directory not empty */ + error = -ENOTEMPTY; + break; + case 0xFF: /* someone deleted file */ + error = -ENOENT; + break; + default: + error = -EACCES; + break; + } out: return error; } @@ -1029,11 +1004,6 @@ static int ncp_unlink(struct inode *dir, struct dentry *dentry) DPRINTK(KERN_DEBUG "ncp_unlink: unlinking %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); - error = -ENOTDIR; - if (!dir || !S_ISDIR(dir->i_mode)) { - printk(KERN_WARNING "ncp_unlink: inode is NULL or not a directory\n"); - goto out; - } error = -EIO; if (!ncp_conn_valid(NCP_SERVER(dir))) goto out; @@ -1050,19 +1020,38 @@ printk(KERN_DEBUG "ncp_unlink: closing file\n"); error = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry); #ifdef CONFIG_NCPFS_STRONG + /* 9C is Invalid path.. It should be 8F, 90 - read only, but + it is not :-( */ if (error == 0x9C && NCP_SERVER(dir)->m.flags & NCP_MOUNT_STRONG) { /* R/O */ error = ncp_force_unlink(dir, dentry); } #endif - if (!error) { - DPRINTK(KERN_DEBUG "ncp: removed %s/%s\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - ncp_invalid_dir_cache(dir); - d_delete(dentry); - } else if (error == 0xFF) { - error = -ENOENT; - } else { - error = -EACCES; + switch (error) { + case 0x00: + DPRINTK(KERN_DEBUG "ncp: removed %s/%s\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + ncp_invalid_dir_cache(dir); + d_delete(dentry); + break; + case 0x85: + case 0x8A: + error = -EACCES; + break; + case 0x8D: /* some files in use */ + case 0x8E: /* all files in use */ + error = -EBUSY; + break; + case 0x8F: /* some read only */ + case 0x90: /* all read only */ + case 0x9C: /* !!! returned when in-use or read-only by NW4 */ + error = -EPERM; + break; + case 0xFF: + error = -ENOENT; + break; + default: + error = -EACCES; + break; } out: @@ -1074,7 +1063,7 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, { int old_len = old_dentry->d_name.len; int new_len = new_dentry->d_name.len; - int error, done_flag=0; + int error; char _old_name[old_dentry->d_name.len + 1]; char _new_name[new_dentry->d_name.len + 1]; @@ -1082,58 +1071,44 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, old_dentry->d_parent->d_name.name, old_dentry->d_name.name, new_dentry->d_parent->d_name.name, new_dentry->d_name.name); - error = -ENOTDIR; - if (!old_dir || !S_ISDIR(old_dir->i_mode)) { - printk(KERN_WARNING "ncp_rename: old inode is NULL or not a directory\n"); - goto out; - } - if (!new_dir || !S_ISDIR(new_dir->i_mode)) { - printk(KERN_WARNING "ncp_rename: new inode is NULL or not a directory\n"); - goto out; - } error = -EIO; if (!ncp_conn_valid(NCP_SERVER(old_dir))) goto out; strncpy(_old_name, old_dentry->d_name.name, old_len); _old_name[old_len] = '\0'; - if (!ncp_preserve_case(old_dir)) { - str_upper(_old_name); - } + io2vol(NCP_SERVER(old_dir), _old_name, !ncp_preserve_case(old_dir)); strncpy(_new_name, new_dentry->d_name.name, new_len); _new_name[new_len] = '\0'; - if (!ncp_preserve_case(new_dir)) { - str_upper(_new_name); - } + io2vol(NCP_SERVER(new_dir), _new_name, !ncp_preserve_case(new_dir)); error = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir), old_dir, _old_name, new_dir, _new_name); #ifdef CONFIG_NCPFS_STRONG - if (error == 0x90 && NCP_SERVER(old_dir)->m.flags & NCP_MOUNT_STRONG) { /* RO */ + if ((error == 0x90 || error == -EACCES) && NCP_SERVER(old_dir)->m.flags & NCP_MOUNT_STRONG) { /* RO */ error = ncp_force_rename(old_dir, old_dentry, _old_name, - new_dir, new_dentry, _new_name, - &done_flag); + new_dir, new_dentry, _new_name); } #endif - if (error == 0) - { - if (done_flag == 0) /* if 1, the following already happened */ - { /* in ncp_force_rename() */ - DPRINTK(KERN_DEBUG "ncp renamed %s -> %s.\n", + switch (error) { + case 0x00: + DPRINTK(KERN_DEBUG "ncp renamed %s -> %s.\n", old_dentry->d_name.name,new_dentry->d_name.name); - ncp_invalid_dir_cache(old_dir); - ncp_invalid_dir_cache(new_dir); - d_move(old_dentry,new_dentry); - } - } else { - if (error == 0x9E) + ncp_invalid_dir_cache(old_dir); + ncp_invalid_dir_cache(new_dir); + /* d_move(old_dentry, new_dentry); */ + break; + case 0x9E: error = -ENAMETOOLONG; - else if (error == 0xFF) + break; + case 0xFF: error = -ENOENT; - else + break; + default: error = -EACCES; + break; } out: return error; @@ -1152,14 +1127,12 @@ extern struct timezone sys_tz; static int utc2local(int time) { - return time - sys_tz.tz_minuteswest * 60 + - (sys_tz.tz_dsttime ? 3600 : 0); + return time - sys_tz.tz_minuteswest * 60; } static int local2utc(int time) { - return time + sys_tz.tz_minuteswest * 60 - - (sys_tz.tz_dsttime ? 3600 : 0); + return time + sys_tz.tz_minuteswest * 60; } /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ @@ -1168,7 +1141,9 @@ ncp_date_dos2unix(unsigned short time, unsigned short date) { int month, year, secs; - month = ((date >> 5) & 15) - 1; + /* first subtract and mask after that... Otherwise, if + date == 0, bad things happen */ + month = ((date >> 5) - 1) & 15; year = date >> 9; secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) + diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 4a0459cdf..1afee6c7e 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -4,6 +4,7 @@ * Copyright (C) 1995, 1996 by Volker Lendecke * Modified for big endian by J.F. Chadima and David S. Miller * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache + * Modified 1998 Wolfram Pienkoss for NLS * */ @@ -27,6 +28,7 @@ #include <linux/init.h> #include <linux/ncp_fs.h> + #include "ncplib_kernel.h" static void ncp_read_inode(struct inode *); @@ -49,6 +51,10 @@ static struct super_operations ncp_sops = }; extern struct dentry_operations ncp_dentry_operations; +#ifdef CONFIG_NCPFS_EXTRAS +extern struct inode_operations ncp_symlink_inode_operations; +extern int ncp_symlink(struct inode*, struct dentry*, const char*); +#endif static struct nw_file_info *read_nwinfo = NULL; static struct semaphore read_sem = MUTEX; @@ -62,6 +68,12 @@ void ncp_update_inode(struct inode *inode, struct nw_file_info *nwinfo) NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum; NCP_FINFO(inode)->volNumber = nwinfo->i.volNumber; +#ifdef CONFIG_NCPFS_SMALLDOS + NCP_FINFO(inode)->origNS = nwinfo->i.NSCreator; +#endif +#ifdef CONFIG_NCPFS_STRONG + NCP_FINFO(inode)->nwattr = nwinfo->i.attributes; +#endif NCP_FINFO(inode)->opened = nwinfo->opened; NCP_FINFO(inode)->access = nwinfo->access; NCP_FINFO(inode)->server_file_handle = nwinfo->server_file_handle; @@ -79,12 +91,42 @@ void ncp_update_inode2(struct inode* inode, struct nw_file_info *nwinfo) struct ncp_server *server = NCP_SERVER(inode); if (!NCP_FINFO(inode)->opened) { +#ifdef CONFIG_NCPFS_STRONG + NCP_FINFO(inode)->nwattr = nwi->attributes; +#endif if (nwi->attributes & aDIR) { inode->i_mode = server->m.dir_mode; inode->i_size = 512; } else { inode->i_mode = server->m.file_mode; inode->i_size = le32_to_cpu(nwi->dataStreamSize); +#ifdef CONFIG_NCPFS_EXTRAS + if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS)) && (nwi->attributes & aSHARED)) { + switch (nwi->attributes & (aHIDDEN|aSYSTEM)) { + case aHIDDEN: + if (server->m.flags & NCP_MOUNT_SYMLINKS) { + if ((inode->i_size >= NCP_MIN_SYMLINK_SIZE) + && (inode->i_size <= NCP_MAX_SYMLINK_SIZE)) { + inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK; + break; + } + } + /* FALLTHROUGH */ + case 0: + if (server->m.flags & NCP_MOUNT_EXTRAS) + inode->i_mode |= 0444; + break; + case aSYSTEM: + if (server->m.flags & NCP_MOUNT_EXTRAS) + inode->i_mode |= (inode->i_mode >> 2) & 0111; + break; + /* case aSYSTEM|aHIDDEN: */ + default: + /* reserved combination */ + break; + } + } +#endif } if (nwi->attributes & aRONLY) inode->i_mode &= ~0222; } @@ -114,6 +156,34 @@ static void ncp_set_attr(struct inode *inode, struct nw_file_info *nwinfo) } else { inode->i_mode = server->m.file_mode; inode->i_size = le32_to_cpu(nwi->dataStreamSize); +#ifdef CONFIG_NCPFS_EXTRAS + if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS)) + && (nwi->attributes & aSHARED)) { + switch (nwi->attributes & (aHIDDEN|aSYSTEM)) { + case aHIDDEN: + if (server->m.flags & NCP_MOUNT_SYMLINKS) { + if ((inode->i_size >= NCP_MIN_SYMLINK_SIZE) + && (inode->i_size <= NCP_MAX_SYMLINK_SIZE)) { + inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK; + break; + } + } + /* FALLTHROUGH */ + case 0: + if (server->m.flags & NCP_MOUNT_EXTRAS) + inode->i_mode |= 0444; + break; + case aSYSTEM: + if (server->m.flags & NCP_MOUNT_EXTRAS) + inode->i_mode |= (inode->i_mode >> 2) & 0111; + break; + /* case aSYSTEM|aHIDDEN: */ + default: + /* reserved combination */ + break; + } + } +#endif } if (nwi->attributes & aRONLY) inode->i_mode &= ~0222; @@ -157,6 +227,10 @@ static void ncp_read_inode(struct inode *inode) inode->i_op = &ncp_file_inode_operations; } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &ncp_dir_inode_operations; +#ifdef CONFIG_NCPFS_EXTRAS + } else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &ncp_symlink_inode_operations; +#endif } else { inode->i_op = NULL; } @@ -211,7 +285,6 @@ static void ncp_init_root(struct ncp_server *server, { struct ncp_inode_info *root = &(server->root); struct nw_info_struct *i = &(root->finfo.i); - unsigned short dummy; DPRINTK(KERN_DEBUG "ncp_init_root: i = %x\n", (int) i); @@ -219,15 +292,13 @@ static void ncp_init_root(struct ncp_server *server, i->dataStreamSize= 1024; i->dirEntNum = 0; i->DosDirNum = 0; +#ifdef CONFIG_NCPFS_SMALLDOS + i->NSCreator = NW_NS_DOS; +#endif i->volNumber = NCP_NUMBER_OF_VOLUMES + 1; /* illegal volnum */ - ncp_date_unix2dos(0, &(i->creationTime), &(i->creationDate)); - ncp_date_unix2dos(0, &(i->modifyTime ), &(i->modifyDate)); - ncp_date_unix2dos(0, &(dummy ), &(i->lastAccessDate)); - i->creationTime = le16_to_cpu(i->creationTime); - i->creationDate = le16_to_cpu(i->creationDate); - i->modifyTime = le16_to_cpu(i->modifyTime); - i->modifyDate = le16_to_cpu(i->modifyDate); - i->lastAccessDate= le16_to_cpu(i->lastAccessDate); + /* set dates of mountpoint to Jan 1, 1986; 00:00 */ + i->creationTime = i->modifyTime = cpu_to_le16(0x0000); + i->creationDate = i->modifyDate = i->lastAccessDate = cpu_to_le16(0x0C21); i->nameLen = 0; i->entryName[0] = '\0'; @@ -308,6 +379,14 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent) server->m.dir_mode = (server->m.dir_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR; +#ifdef CONFIG_NCPFS_NLS + /* load the default NLS charsets */ + server->nls_charsets.codepage[0] = 0; + server->nls_charsets.iocharset[0] = 0; + server->nls_vol = load_nls_default(); + server->nls_io = load_nls_default(); +#endif /* CONFIG_NCPFS_NLS */ + server->packet_size = NCP_PACKET_SIZE; server->packet = ncp_kmalloc(NCP_PACKET_SIZE, GFP_KERNEL); if (server->packet == NULL) @@ -377,6 +456,10 @@ out_free_packet: out_no_packet: printk(KERN_ERR "ncp_read_super: could not alloc packet\n"); out_free_server: +#ifdef CONFIG_NCPFS_NLS + unload_nls(server->nls_io); + unload_nls(server->nls_vol); +#endif ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server)); goto out_unlock; out_no_server: @@ -416,6 +499,20 @@ static void ncp_put_super(struct super_block *sb) ncp_disconnect(server); ncp_unlock_server(server); +#ifdef CONFIG_NCPFS_NLS + /* unload the NLS charsets */ + if (server->nls_vol) + { + unload_nls(server->nls_vol); + server->nls_vol = NULL; + } + if (server->nls_io) + { + unload_nls(server->nls_io); + server->nls_io = NULL; + } +#endif /* CONFIG_NCPFS_NLS */ + fput(server->ncp_filp); kill_proc(server->m.wdog_pid, SIGTERM, 1); @@ -457,9 +554,12 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) int result = 0; int info_mask; struct nw_modify_dos_info info; + struct ncp_server *server; result = -EIO; - if (!ncp_conn_valid(NCP_SERVER(inode))) + + server = NCP_SERVER(inode); + if ((!server) || !ncp_conn_valid(server)) goto out; result = inode_change_ok(inode, attr); @@ -468,11 +568,11 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) result = -EPERM; if (((attr->ia_valid & ATTR_UID) && - (attr->ia_uid != NCP_SERVER(inode)->m.uid))) + (attr->ia_uid != server->m.uid))) goto out; if (((attr->ia_valid & ATTR_GID) && - (attr->ia_uid != NCP_SERVER(inode)->m.gid))) + (attr->ia_gid != server->m.gid))) goto out; if (((attr->ia_valid & ATTR_MODE) && @@ -486,26 +586,53 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) #if 1 if ((attr->ia_valid & ATTR_MODE) != 0) { - if (!S_ISREG(inode->i_mode)) + if (S_ISDIR(inode->i_mode)) { + umode_t newmode; + + info_mask |= DM_ATTRIBUTES; + newmode = attr->ia_mode; + newmode &= NCP_SERVER(inode)->m.dir_mode; + + if (newmode & 0222) + info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); + else + info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); + } else if (!S_ISREG(inode->i_mode)) { return -EPERM; } else { umode_t newmode; - +#ifdef CONFIG_NCPFS_EXTRAS + int extras; + + extras = server->m.flags & NCP_MOUNT_EXTRAS; +#endif info_mask |= DM_ATTRIBUTES; newmode=attr->ia_mode; - newmode &= NCP_SERVER(inode)->m.file_mode; +#ifdef CONFIG_NCPFS_EXTRAS + if (!extras) +#endif + newmode &= server->m.file_mode; if (newmode & 0222) /* any write bit set */ { - info.attributes &= ~0x60001; + info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); } else { - info.attributes |= 0x60001; + info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); } +#ifdef CONFIG_NCPFS_EXTRAS + if (extras) { + if (newmode & 0111) /* any execute bit set */ + info.attributes |= aSHARED | aSYSTEM; + /* read for group/world and not in default file_mode */ + else if (newmode & ~server->m.file_mode & 0444) + info.attributes |= aSHARED; + } +#endif } } #endif @@ -546,6 +673,10 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) result = 0; } } +#ifdef CONFIG_NCPFS_STRONG + if ((!result) && (info_mask & DM_ATTRIBUTES)) + NCP_FINFO(inode)->nwattr = info.attributes; +#endif } if ((attr->ia_valid & ATTR_SIZE) != 0) { int written; diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index 2df6fee09..8ada3752b 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -3,6 +3,7 @@ * * Copyright (C) 1995, 1996 by Volker Lendecke * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache + * Modified 1998 Wolfram Pienkoss for NLS * */ @@ -17,6 +18,7 @@ #include <linux/ncp.h> #include <linux/ncp_fs.h> + #include "ncplib_kernel.h" /* maximum limit for ncp_objectname_ioctl */ @@ -485,6 +487,69 @@ int ncp_ioctl(struct inode *inode, struct file *filp, return 0; } #endif /* CONFIG_NCPFS_NDS_DOMAINS */ + +#ifdef CONFIG_NCPFS_NLS +/* Here we are select the iocharset and the codepage for NLS. + * Thanks Petr Vandrovec for idea and many hints. + */ + case NCP_IOC_SETCHARSETS: + if ( (permission(inode, MAY_WRITE) != 0) + && (current->uid != server->m.mounted_uid)) + { + return -EACCES; + } + if (server->root_setuped) return -EBUSY; + { + struct ncp_nls_ioctl user; + struct nls_table *codepage; + struct nls_table *iocharset; + struct nls_table *oldset_io; + struct nls_table *oldset_cp; + + if (copy_from_user(&user, + (struct ncp_nls_ioctl*)arg, + sizeof(user))) return -EFAULT; + + codepage = NULL; + if (!user.codepage[0]) { + codepage = load_nls_default(); + } + else { + codepage = load_nls(user.codepage); + if (! codepage) { + return -EBADRQC; + } + } + + iocharset = NULL; + if (user.iocharset[0] == 0) { + iocharset = load_nls_default(); + } + else { + iocharset = load_nls(user.iocharset); + if (! iocharset) { + unload_nls(codepage); + return -EBADRQC; + } + } + + oldset_cp = server->nls_vol; + server->nls_vol = codepage; + oldset_io = server->nls_io; + server->nls_io = iocharset; + server->nls_charsets = user; + if (oldset_cp) unload_nls(oldset_cp); + if (oldset_io) unload_nls(oldset_io); + return 0; + } + + case NCP_IOC_GETCHARSETS: /* not tested */ + if (copy_to_user((struct ncp_nls_ioctl*)arg, + &(server->nls_charsets), + sizeof(server->nls_charsets))) return -EFAULT; + return 0; +#endif /* CONFIG_NCPFS_NLS */ + default: return -EINVAL; } diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index 2c7610d0b..6b321e6c9 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -137,8 +137,6 @@ int ncp_mmap(struct file *file, struct vm_area_struct *vma) inode->i_atime = CURRENT_TIME; } - vma->vm_file = file; - file->f_count++; vma->vm_ops = &ncp_file_mmap; return 0; } diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c index fc0bbf013..bb034a4e4 100644 --- a/fs/ncpfs/ncplib_kernel.c +++ b/fs/ncpfs/ncplib_kernel.c @@ -238,7 +238,8 @@ NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum, err); } static void ncp_add_handle_path(struct ncp_server *server, __u8 vol_num, - __u32 dir_base, int have_dir_base, char *path) + __u32 dir_base, int have_dir_base, + const char *path) { ncp_add_byte(server, vol_num); ncp_add_dword(server, dir_base); @@ -468,12 +469,17 @@ ncp_lookup_volume(struct ncp_server *server, char *volname, target->nameLen = strlen(volname); strcpy(target->entryName, volname); target->attributes = aDIR; + /* set dates to Jan 1, 1986 00:00 */ + target->creationTime = target->modifyTime = cpu_to_le16(0x0000); + target->creationDate = target->modifyDate = target->lastAccessDate = cpu_to_le16(0x0C21); return 0; } -int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, - struct inode *dir, __u32 info_mask, - struct nw_modify_dos_info *info) +int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *server, + struct inode *dir, + const char *path, + __u32 info_mask, + const struct nw_modify_dos_info *info) { __u8 volnum = NCP_FINFO(dir)->volNumber; __u32 dirent = NCP_FINFO(dir)->dirEntNum; @@ -487,13 +493,22 @@ int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, ncp_add_dword(server, info_mask); ncp_add_mem(server, info, sizeof(*info)); - ncp_add_handle_path(server, volnum, dirent, 1, NULL); + ncp_add_handle_path(server, volnum, dirent, 1, path); result = ncp_request(server, 87); ncp_unlock_server(server); return result; } +int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, + struct inode *dir, + __u32 info_mask, + const struct nw_modify_dos_info *info) +{ + return ncp_modify_file_or_subdir_dos_info_path(server, dir, NULL, + info_mask, info); +} + static int ncp_DeleteNSEntry(struct ncp_server *server, __u8 have_dir_base, __u8 volnum, __u32 dirent, @@ -788,6 +803,35 @@ out: return result; } +#ifdef CONFIG_NCPFS_EXTRAS +int +ncp_read_kernel(struct ncp_server *server, const char *file_id, + __u32 offset, __u16 to_read, char *target, int *bytes_read) { + int error; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(get_ds()); + error = ncp_read(server, file_id, offset, to_read, target, bytes_read); + set_fs(old_fs); + return error; +} + +int +ncp_write_kernel(struct ncp_server *server, const char *file_id, + __u32 offset, __u16 to_write, + const char *source, int *bytes_written) { + int error; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(get_ds()); + error = ncp_write(server, file_id, offset, to_write, source, bytes_written); + set_fs(old_fs); + return error; +} +#endif + #ifdef CONFIG_NCPFS_IOCTL_LOCKING int ncp_LogPhysicalRecord(struct ncp_server *server, const char *file_id, diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index 7834c6c4f..cc1df1896 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h @@ -4,6 +4,7 @@ * Copyright (C) 1995, 1996 by Volker Lendecke * Modified for big endian by J.F. Chadima and David S. Miller * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache + * Modified 1998 Wolfram Pienkoss for NLS * */ @@ -23,6 +24,10 @@ #include <asm/unaligned.h> #include <asm/string.h> +#ifdef CONFIG_NCPFS_NLS +#include <linux/nls.h> +#endif + #include <linux/ncp.h> #include <linux/ncp_fs.h> #include <linux/ncp_fs_sb.h> @@ -36,12 +41,19 @@ int ncp_close_file(struct ncp_server *, const char *); int ncp_read(struct ncp_server *, const char *, __u32, __u16, char *, int *); int ncp_write(struct ncp_server *, const char *, __u32, __u16, const char *, int *); +#ifdef CONFIG_NCPFS_EXTRAS +int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16, char *, int *); +int ncp_write_kernel(struct ncp_server *, const char *, __u32, __u16, + const char *, int *); +#endif int ncp_obtain_info(struct ncp_server *server, struct inode *, char *, struct nw_info_struct *target); int ncp_lookup_volume(struct ncp_server *, char *, struct nw_info_struct *); int ncp_modify_file_or_subdir_dos_info(struct ncp_server *, struct inode *, - __u32, struct nw_modify_dos_info *info); + __u32, const struct nw_modify_dos_info *info); +int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *, struct inode *, + const char* path, __u32, const struct nw_modify_dos_info *info); int ncp_del_file_or_subdir2(struct ncp_server *, struct dentry*); int ncp_del_file_or_subdir(struct ncp_server *, struct inode *, char *); @@ -75,4 +87,83 @@ int ncp_mount_subdir(struct ncp_server* server, __u8 volNumber, __u8 srcNS, __u32 srcDirEntNum); #endif /* CONFIG_NCPFS_MOUNT_SUBDIR */ + +#ifdef CONFIG_NCPFS_NLS +/* This are the NLS conversion routines with inspirations and code parts + * from the vfat file system and hints from Petr Vandrovec. + */ + +/* + * It should be replaced by charset specifc conversion. Gordon Chaffee + * has prepared some things, but I don't know, what he thinks about it. + * The conversion tables for the io charsets should be generatable by + * Unicode table, shouldn't it? I have written so generation code for it. + * The tables for the vendor specific codepages...? Hmm. The Samba sources + * contains also any hints. + */ + +#define toupperif(c, u) ((((u) != 0) && ((c) >= 'a') && ((c) <= 'z')) \ + ? (c)-('a'-'A') : (c)) +#define tolowerif(c, u) ((((u) != 0) && ((c) >= 'A') && ((c) <= 'Z')) \ + ? (c)-('A'-'a') : (c)) + +static inline void +io2vol(struct ncp_server *server, char *name, int case_trans) +{ + unsigned char nc; + unsigned char *np; + unsigned char *up; + struct nls_unicode uc; + struct nls_table *nls_in; + struct nls_table *nls_out; + + nls_in = server->nls_io; + nls_out = server->nls_vol; + np = name; + + while (*np) + { + nc = 0; + uc = nls_in->charset2uni[toupperif(*np, case_trans)]; + up = nls_out->page_uni2charset[uc.uni2]; + if (up != NULL) nc = up[uc.uni1]; + if (nc != 0) *np = nc; + np++; + } +} + +static inline void +vol2io(struct ncp_server *server, char *name, int case_trans) +{ + unsigned char nc; + unsigned char *np; + unsigned char *up; + struct nls_unicode uc; + struct nls_table *nls_in; + struct nls_table *nls_out; + + nls_in = server->nls_vol; + nls_out = server->nls_io; + np = name; + + while (*np) + { + nc = 0; + uc = nls_in->charset2uni[*np]; + up = nls_out->page_uni2charset[uc.uni2]; + if (up != NULL) nc = up[uc.uni1]; + if (nc == 0) nc = *np; + *np = tolowerif(nc, case_trans); + np++; + } +} + +#else + +#define io2vol(S,N,U) if (U) str_upper(N) +#define vol2io(S,N,U) if (U) str_lower(N) + +#endif /* CONFIG_NCPFS_NLS */ + #endif /* _NCPLIB_H */ + diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c index cdcfc4220..865fc68a3 100644 --- a/fs/ncpfs/sock.c +++ b/fs/ncpfs/sock.c @@ -94,7 +94,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size) poll_table wait_table; struct poll_table_entry entry; int init_timeout, max_timeout; - int timeout; long tmp_timeout; + int timeout; int retrans; int major_timeout_seen; int acknowledge_seen; diff --git a/fs/ncpfs/symlink.c b/fs/ncpfs/symlink.c new file mode 100644 index 000000000..a923c218c --- /dev/null +++ b/fs/ncpfs/symlink.c @@ -0,0 +1,212 @@ +/* + * linux/fs/ncpfs/symlink.c + * + * Code for allowing symbolic links on NCPFS (i.e. NetWare) + * Symbolic links are not supported on native NetWare, so we use an + * infrequently-used flag (Sh) and store a two-word magic header in + * the file to make sure we don't accidentally use a non-link file + * as a link. + * + * from linux/fs/ext2/symlink.c + * + * Copyright (C) 1998-99, Frank A. Vorstenbosch + * + * ncpfs symlink handling code + * NLS support (c) 1999 Petr Vandrovec + * + */ + +#include <linux/config.h> + +#ifdef CONFIG_NCPFS_EXTRAS + +#include <asm/uaccess.h> +#include <asm/segment.h> + +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/ncp_fs.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/stat.h> +#include "ncplib_kernel.h" + + +/* these magic numbers must appear in the symlink file -- this makes it a bit + more resilient against the magic attributes being set on random files. */ + +#define NCP_SYMLINK_MAGIC0 le32_to_cpu(0x6c6d7973) /* "symlnk->" */ +#define NCP_SYMLINK_MAGIC1 le32_to_cpu(0x3e2d6b6e) + +static int ncp_readlink(struct dentry *, char *, int); +static struct dentry *ncp_follow_link(struct dentry *, struct dentry *, unsigned int); +int ncp_create_new(struct inode *dir, struct dentry *dentry, + int mode,int attributes); + +/* + * symlinks can't do much... + */ +struct inode_operations ncp_symlink_inode_operations={ + NULL, /* no file-operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + ncp_readlink, /* readlink */ + ncp_follow_link, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL /* smap */ +}; + +/* ----- follow a symbolic link ------------------------------------------ */ + +static struct dentry *ncp_follow_link(struct dentry *dentry, + struct dentry *base, + unsigned int follow) +{ + struct inode *inode=dentry->d_inode; + int error, length, cnt; + char *link; + +#ifdef DEBUG + printk("ncp_follow_link(dentry=%p,base=%p,follow=%u)\n",dentry,base,follow); +#endif + + if(!S_ISLNK(inode->i_mode)) { + dput(base); + return ERR_PTR(-EINVAL); + } + + if(ncp_make_open(inode,O_RDONLY)) { + dput(base); + return ERR_PTR(-EIO); + } + + for (cnt = 0; (link=(char *)kmalloc(NCP_MAX_SYMLINK_SIZE+1, GFP_NFS))==NULL; cnt++) { + if (cnt > 10) { + dput(base); + return ERR_PTR(-EAGAIN); /* -ENOMEM? */ + } + schedule(); + } + + error=ncp_read_kernel(NCP_SERVER(inode),NCP_FINFO(inode)->file_handle, + 0,NCP_MAX_SYMLINK_SIZE,link,&length); + + if (error!=0 || length<NCP_MIN_SYMLINK_SIZE || + ((__u32 *)link)[0]!=NCP_SYMLINK_MAGIC0 || ((__u32 *)link)[1]!=NCP_SYMLINK_MAGIC1) { + dput(base); + kfree(link); + return ERR_PTR(-EIO); + } + + link[length]=0; + + vol2io(NCP_SERVER(inode), link+8, 0); + + /* UPDATE_ATIME(inode); */ + base=lookup_dentry(link+8, base, follow); + kfree(link); + + return base; +} + +/* ----- read symbolic link ---------------------------------------------- */ + +static int ncp_readlink(struct dentry * dentry, char * buffer, int buflen) +{ + struct inode *inode=dentry->d_inode; + char *link; + int length,error; + +#ifdef DEBUG + printk("ncp_readlink(dentry=%p,buffer=%p,buflen=%d)\n",dentry,buffer,buflen); +#endif + + if(!S_ISLNK(inode->i_mode)) + return -EINVAL; + + if(ncp_make_open(inode,O_RDONLY)) + return -EIO; + + if((link=(char *)kmalloc(NCP_MAX_SYMLINK_SIZE+1,GFP_NFS))==NULL) + return -ENOMEM; + + error = ncp_read_kernel(NCP_SERVER(inode),NCP_FINFO(inode)->file_handle, + 0,NCP_MAX_SYMLINK_SIZE,link,&length); + + if (error!=0 || length < NCP_MIN_SYMLINK_SIZE || buflen < (length-8) || + ((__u32 *)link)[0]!=NCP_SYMLINK_MAGIC0 ||((__u32 *)link)[1]!=NCP_SYMLINK_MAGIC1) { + error = -EIO; + goto out; + } + + link[length] = 0; + + vol2io(NCP_SERVER(inode), link+8, 0); + + error = length - 8; + if(copy_to_user(buffer, link+8, error)) + error = -EFAULT; + +out:; + kfree(link); + return error; +} + +/* ----- create a new symbolic link -------------------------------------- */ + +int ncp_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { + int i,length; + struct inode *inode; + char *link; + +#ifdef DEBUG + printk("ncp_symlink(dir=%p,dentry=%p,symname=%s)\n",dir,dentry,symname); +#endif + + if (!(NCP_SERVER(dir)->m.flags & NCP_MOUNT_SYMLINKS)) + return -EPERM; /* EPERM is returned by VFS if symlink procedure does not exist */ + + if ((length=strlen(symname))>NCP_MAX_SYMLINK_SIZE) + return -EINVAL; + + if ((link=(char *)kmalloc(length+9,GFP_NFS))==NULL) + return -ENOMEM; + + if (ncp_create_new(dir,dentry,0,aSHARED|aHIDDEN)) { + kfree(link); + return -EIO; + } + + inode=dentry->d_inode; + + ((__u32 *)link)[0]=NCP_SYMLINK_MAGIC0; + ((__u32 *)link)[1]=NCP_SYMLINK_MAGIC1; + memcpy(link+8, symname, length+1); /* including last zero for io2vol */ + + /* map to/from server charset, do not touch upper/lower case as + symlink can point out of ncp filesystem */ + io2vol(NCP_SERVER(inode), link+8, 0); + + if(ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, + 0, length+8, link, &i) || i!=length+8) { + kfree(link); + return -EIO; + } + + kfree(link); + return 0; +} +#endif + +/* ----- EOF ----- */ diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index c71893476..ef5bc6ea9 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -51,10 +51,9 @@ struct nfs_dirent { static int nfs_safe_remove(struct dentry *); -static int nfs_dir_open(struct inode *, struct file *); static ssize_t nfs_dir_read(struct file *, char *, size_t, loff_t *); static int nfs_readdir(struct file *, void *, filldir_t); -static int nfs_lookup(struct inode *, struct dentry *); +static struct dentry *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 *); @@ -73,9 +72,9 @@ static struct file_operations nfs_dir_operations = { NULL, /* select - default */ NULL, /* ioctl - default */ NULL, /* mmap */ - nfs_dir_open, /* open - revalidate */ + nfs_open, /* open */ NULL, /* flush */ - NULL, /* no special release code */ + nfs_release, /* release */ NULL /* fsync */ }; @@ -102,16 +101,6 @@ struct inode_operations nfs_dir_inode_operations = { nfs_revalidate, /* revalidate */ }; -static int -nfs_dir_open(struct inode *dir, struct file *file) -{ - struct dentry *dentry = file->f_dentry; - - dfprintk(VFS, "NFS: nfs_dir_open(%s/%s)\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - return nfs_revalidate_inode(NFS_DSERVER(dentry), dentry); -} - static ssize_t nfs_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos) { @@ -147,11 +136,6 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) dfprintk(VFS, "NFS: nfs_readdir(%s/%s)\n", dentry->d_parent->d_name.name, dentry->d_name.name); - result = -EBADF; - if (!inode || !S_ISDIR(inode->i_mode)) { - printk("nfs_readdir: inode is NULL or not a directory\n"); - goto out; - } result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry); if (result < 0) @@ -380,7 +364,48 @@ static inline void nfs_renew_times(struct dentry * dentry) dentry->d_time = jiffies; } -#define NFS_REVALIDATE_INTERVAL (5*HZ) +static inline int nfs_dentry_force_reval(struct dentry *dentry, int flags) +{ + struct inode *inode = dentry->d_inode; + unsigned long timeout = NFS_ATTRTIMEO(inode); + + /* + * If it's the last lookup in a series, we use a stricter + * cache consistency check by looking at the parent mtime. + * + * If it's been modified in the last hour, be really strict. + * (This still means that we can avoid doing unnecessary + * work on directories like /usr/share/bin etc which basically + * never change). + */ + if (!(flags & LOOKUP_CONTINUE)) { + long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime; + + if (diff < 15*60) + timeout = 0; + } + + return time_after(jiffies,dentry->d_time + timeout); +} + +/* + * We judge how long we want to trust negative + * dentries by looking at the parent inode mtime. + * + * If mtime is close to present time, we revalidate + * more often. + */ +static inline int nfs_neg_need_reval(struct dentry *dentry) +{ + unsigned long timeout = 30 * HZ; + long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime; + + if (diff < 5*60) + timeout = 1 * HZ; + + return time_after(jiffies, dentry->d_time + timeout); +} + /* * This is called every time the dcache has a lookup hit, * and we should check whether we can really trust that @@ -393,43 +418,41 @@ static inline void nfs_renew_times(struct dentry * dentry) * we do a new lookup and verify that the dentry is still * correct. */ -static int nfs_lookup_revalidate(struct dentry * dentry) +static int nfs_lookup_revalidate(struct dentry * dentry, int flags) { struct dentry * parent = dentry->d_parent; struct inode * inode = dentry->d_inode; - unsigned long time = jiffies - dentry->d_time; int error; struct nfs_fh fhandle; struct nfs_fattr fattr; /* - * If we don't have an inode, let's just assume - * a 5-second "live" time for negative dentries. + * If we don't have an inode, let's look at the parent + * directory mtime to get a hint about how often we + * should validate things.. */ if (!inode) { - if (time < NFS_REVALIDATE_INTERVAL) - goto out_valid; - goto out_bad; + if (nfs_neg_need_reval(dentry)) + goto out_bad; + goto out_valid; } if (is_bad_inode(inode)) { -#ifdef NFS_PARANOIA -printk("nfs_lookup_validate: %s/%s has dud inode\n", -parent->d_name.name, dentry->d_name.name); -#endif + dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n", + parent->d_name.name, dentry->d_name.name); goto out_bad; } - if (time < NFS_ATTRTIMEO(inode)) + if (IS_ROOT(dentry)) goto out_valid; - if (IS_ROOT(dentry)) + if (!nfs_dentry_force_reval(dentry, flags)) goto out_valid; /* * Do a new lookup and check the dentry attributes. */ - error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent), + error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent), dentry->d_name.name, &fhandle, &fattr); if (error) goto out_bad; @@ -439,8 +462,10 @@ parent->d_name.name, dentry->d_name.name); goto out_bad; /* Filehandle matches? */ - if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) - goto out_bad; + if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) { + if (dentry->d_count < 2) + goto out_bad; + } /* Ok, remeber that we successfully checked it.. */ nfs_renew_times(dentry); @@ -449,6 +474,10 @@ parent->d_name.name, dentry->d_name.name); out_valid: return 1; out_bad: + if (dentry->d_parent->d_inode) + nfs_invalidate_dircache(dentry->d_parent->d_inode); + if (inode && S_ISDIR(inode->i_mode)) + nfs_invalidate_dircache(inode); return 0; } @@ -502,7 +531,7 @@ static void nfs_dentry_release(struct dentry *dentry) } struct dentry_operations nfs_dentry_operations = { - nfs_lookup_revalidate, /* d_validate(struct dentry *) */ + nfs_lookup_revalidate, /* d_revalidate(struct dentry *, int) */ NULL, /* d_hash */ NULL, /* d_compare */ nfs_dentry_delete, /* d_delete(struct dentry *) */ @@ -533,7 +562,7 @@ static void show_dentry(struct list_head * dlist) } #endif -static int nfs_lookup(struct inode *dir, struct dentry * dentry) +static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry) { struct inode *inode; int error; @@ -579,7 +608,7 @@ show_dentry(&inode->i_dentry); } } out: - return error; + return ERR_PTR(error); } /* @@ -624,10 +653,6 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode) dfprintk(VFS, "NFS: create(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); - error = -ENAMETOOLONG; - if (dentry->d_name.len > NFS_MAXNAMLEN) - goto out; - sattr.mode = mode; sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; @@ -642,7 +667,6 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode) error = nfs_instantiate(dentry, &fhandle, &fattr); if (error) d_drop(dentry); -out: return error; } @@ -659,9 +683,6 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); - if (dentry->d_name.len > NFS_MAXNAMLEN) - return -ENAMETOOLONG; - sattr.mode = mode; sattr.uid = sattr.gid = sattr.size = (unsigned) -1; if (S_ISCHR(mode) || S_ISBLK(mode)) @@ -691,10 +712,6 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); - error = -ENAMETOOLONG; - if (dentry->d_name.len > NFS_MAXNAMLEN) - goto out; - sattr.mode = mode | S_IFDIR; sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; @@ -709,21 +726,9 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) nfs_invalidate_dircache(dir); error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent), dentry->d_name.name, &sattr, &fhandle, &fattr); -out: return error; } -/* - * To avoid retaining a stale inode reference, we check the dentry - * use count prior to the operation, and return EBUSY if it has - * multiple users. - * - * We update inode->i_nlink and free the inode prior to the operation - * to avoid possible races if the server reuses the inode. - * - * FIXME! We don't do it anymore (2.1.131) - it interacts badly with - * new rmdir(). -- AV - */ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) { int error; @@ -731,14 +736,6 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); - error = -ENAMETOOLONG; - if (dentry->d_name.len > NFS_MAXNAMLEN) - goto out; - - error = -EBUSY; - if (!list_empty(&dentry->d_hash)) - goto out; - #ifdef NFS_PARANOIA if (dentry->d_inode->i_count > 1) printk("nfs_rmdir: %s/%s inode busy?? i_count=%d, i_nlink=%d\n", @@ -746,15 +743,17 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_inode->i_count, dentry->d_inode->i_nlink); #endif - /* - * Update i_nlink and free the inode before unlinking. - */ - if (dentry->d_inode->i_nlink) - dentry->d_inode->i_nlink --; nfs_invalidate_dircache(dir); error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name); -out: + + /* Update i_nlink and invalidate dentry. */ + if (!error) { + d_drop(dentry); + if (dentry->d_inode->i_nlink) + dentry->d_inode->i_nlink --; + } + return error; } @@ -794,7 +793,7 @@ struct dentry *nfs_silly_lookup(struct dentry *parent, char *silly, int slen) { struct qstr sqstr; struct dentry *sdentry; - int error; + struct dentry *res; sqstr.name = silly; sqstr.len = slen; @@ -804,10 +803,10 @@ struct dentry *nfs_silly_lookup(struct dentry *parent, char *silly, int slen) sdentry = d_alloc(parent, &sqstr); if (sdentry == NULL) return ERR_PTR(-ENOMEM); - error = nfs_lookup(parent->d_inode, sdentry); - if (error) { + res = nfs_lookup(parent->d_inode, sdentry); + if (res) { dput(sdentry); - return ERR_PTR(error); + return res; } } return sdentry; @@ -964,10 +963,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry) dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n", dir->i_dev, dir->i_ino, dentry->d_name.name); - error = -ENAMETOOLONG; - if (dentry->d_name.len > NFS_MAXNAMLEN) - goto out; - error = nfs_sillyrename(dir, dentry); if (error && error != -EBUSY) { error = nfs_safe_remove(dentry); @@ -975,7 +970,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry) nfs_renew_times(dentry); } } -out: return error; } @@ -989,9 +983,6 @@ nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) dir->i_dev, dir->i_ino, dentry->d_name.name, symname); error = -ENAMETOOLONG; - if (dentry->d_name.len > NFS_MAXNAMLEN) - goto out; - if (strlen(symname) > NFS_MAXPATHLEN) goto out; @@ -1038,10 +1029,6 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) old_dentry->d_parent->d_name.name, old_dentry->d_name.name, dentry->d_parent->d_name.name, dentry->d_name.name); - error = -ENAMETOOLONG; - if (dentry->d_name.len > NFS_MAXNAMLEN) - goto out; - /* * Drop the dentry in advance to force a new lookup. * Since nfs_proc_link doesn't return a file handle, @@ -1058,7 +1045,6 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) */ inode->i_nlink++; } -out: return error; } @@ -1099,11 +1085,6 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, new_dentry->d_parent->d_name.name, new_dentry->d_name.name, new_dentry->d_count); - error = -ENAMETOOLONG; - if (old_dentry->d_name.len > NFS_MAXNAMLEN || - new_dentry->d_name.len > NFS_MAXNAMLEN) - goto out; - /* * First check whether the target is busy ... we can't * safely do _any_ rename if the target is in use. @@ -1112,28 +1093,24 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, * silly-rename. If the silly-rename succeeds, the * copied dentry is hashed and becomes the new target. * - * For directories, prune any unused children. + * With directories check is done in VFS. */ error = -EBUSY; if (new_dentry->d_count > 1 && new_inode) { - if (S_ISREG(new_inode->i_mode)) { - int err; - /* copy the target dentry's name */ - dentry = d_alloc(new_dentry->d_parent, - &new_dentry->d_name); - if (!dentry) - goto out; - - /* silly-rename the existing target ... */ - err = nfs_sillyrename(new_dir, new_dentry); - if (!err) { - new_dentry = dentry; - new_inode = NULL; - /* hash the replacement target */ - d_add(new_dentry, NULL); - } - } else if (!list_empty(&new_dentry->d_subdirs)) { - shrink_dcache_parent(new_dentry); + int err; + /* copy the target dentry's name */ + dentry = d_alloc(new_dentry->d_parent, + &new_dentry->d_name); + if (!dentry) + goto out; + + /* silly-rename the existing target ... */ + err = nfs_sillyrename(new_dir, new_dentry); + if (!err) { + new_dentry = dentry; + new_inode = NULL; + /* hash the replacement target */ + d_add(new_dentry, NULL); } /* dentry still busy? */ @@ -1208,7 +1185,7 @@ new_inode->i_count, new_inode->i_nlink); error = nfs_proc_rename(NFS_DSERVER(old_dentry), NFS_FH(old_dentry->d_parent), old_dentry->d_name.name, NFS_FH(new_dentry->d_parent), new_dentry->d_name.name); - if (!error) { + if (!error && !S_ISDIR(old_inode->i_mode)) { /* Update the dcache if needed */ if (rehash) d_add(new_dentry, NULL); diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 25caa03d3..1cf40d3ae 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -36,7 +36,6 @@ static int nfs_file_mmap(struct file *, struct vm_area_struct *); static ssize_t nfs_file_read(struct file *, char *, size_t, loff_t *); static ssize_t nfs_file_write(struct file *, const char *, size_t, loff_t *); static int nfs_file_flush(struct file *); -static int nfs_file_close(struct inode *, struct file *); static int nfs_fsync(struct file *, struct dentry *dentry); static struct file_operations nfs_file_operations = { @@ -47,9 +46,9 @@ static struct file_operations nfs_file_operations = { NULL, /* select - default */ NULL, /* ioctl - default */ nfs_file_mmap, /* mmap */ - NULL, /* no special open is needed */ + nfs_open, /* open */ nfs_file_flush, /* flush */ - nfs_file_close, /* release */ + nfs_release, /* release */ nfs_fsync, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ @@ -86,35 +85,23 @@ struct inode_operations nfs_file_inode_operations = { #endif /* - * Sync the file.. - */ -static int -nfs_file_close(struct inode *inode, struct file *file) -{ - int status; - - dfprintk(VFS, "nfs: close(%x/%ld)\n", inode->i_dev, inode->i_ino); - - status = nfs_wb_all(inode); - if (!status) - status = nfs_write_error(inode); - return status; -} - -/* * Flush all dirty pages, and check for write errors. * - * We should probably do this better - this does get called at every - * close, so we should probably just flush the changes that "this" - * file has done and report on only those. - * - * Right now we use the "flush everything" approach. Overkill, but - * works. */ static int nfs_file_flush(struct file *file) { - return nfs_file_close(file->f_dentry->d_inode, file); + struct inode *inode = file->f_dentry->d_inode; + int status; + + dfprintk(VFS, "nfs: flush(%x/%ld)\n", inode->i_dev, inode->i_ino); + + status = nfs_wb_file(inode, file); + if (!status) { + status = file->f_error; + file->f_error = 0; + } + return status; } static ssize_t @@ -161,9 +148,11 @@ nfs_fsync(struct file *file, struct dentry *dentry) dfprintk(VFS, "nfs: fsync(%x/%ld)\n", inode->i_dev, inode->i_ino); - status = nfs_wb_pid(inode, current->pid); - if (!status) - status = nfs_write_error(inode); + status = nfs_wb_file(inode, file); + if (!status) { + status = file->f_error; + file->f_error = 0; + } return status; } @@ -193,10 +182,7 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) if (!count) goto out; - /* Check for an error from a previous async call */ - result = nfs_write_error(inode); - if (!result) - result = generic_file_write(file, buf, count, ppos); + result = generic_file_write(file, buf, count, ppos); out: return result; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 72130244a..c46eeb57b 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -37,6 +37,8 @@ #define NFS_PARANOIA 1 static struct inode * __nfs_fhget(struct super_block *, struct nfs_fattr *); +static void nfs_zap_caches(struct inode *); +static void nfs_invalidate_inode(struct inode *); static void nfs_read_inode(struct inode *); static void nfs_put_inode(struct inode *); @@ -224,6 +226,8 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) lock_super(sb); + sb->s_flags |= MS_ODD_RENAME; /* This should go away */ + sb->s_magic = NFS_SUPER_MAGIC; sb->s_op = &nfs_sops; sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits); @@ -232,6 +236,11 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) server->rsize = nfs_block_size(data->rsize, NULL); server->wsize = nfs_block_size(data->wsize, NULL); server->flags = data->flags; + + if (data->flags & NFS_MOUNT_NOAC) { + data->acregmin = data->acregmax = 0; + data->acdirmin = data->acdirmax = 0; + } server->acregmin = data->acregmin*HZ; server->acregmax = data->acregmax*HZ; server->acdirmin = data->acdirmin*HZ; @@ -386,25 +395,61 @@ nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) * could cause file corruption. But since the dentry * count is 0 and all pending IO for a dentry has been * flushed when the count went to 0, we're safe here. + * Also returns the number of unhashed dentries */ -void nfs_free_dentries(struct inode *inode) +static int +nfs_free_dentries(struct inode *inode) { struct list_head *tmp, *head = &inode->i_dentry; + int unhashed; restart: tmp = head; + unhashed = 0; while ((tmp = tmp->next) != head) { struct dentry *dentry = list_entry(tmp, struct dentry, d_alias); -printk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, -dentry->d_count, !list_empty(&dentry->d_hash)); + dprintk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + dentry->d_count, !list_empty(&dentry->d_hash)); if (!dentry->d_count) { dget(dentry); d_drop(dentry); dput(dentry); goto restart; } + if (!list_empty(&dentry->d_hash)) + unhashed++; } + return unhashed; +} + +/* + * Invalidate the local caches + */ +static void +nfs_zap_caches(struct inode *inode) +{ + NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); + NFS_CACHEINV(inode); + + if (S_ISDIR(inode->i_mode)) + nfs_invalidate_dircache(inode); + else + invalidate_inode_pages(inode); +} + +/* + * Invalidate, but do not unhash, the inode + */ +static void +nfs_invalidate_inode(struct inode *inode) +{ + umode_t save_mode = inode->i_mode; + + make_bad_inode(inode); + inode->i_mode = save_mode; + nfs_inval(inode); + nfs_zap_caches(inode); } /* @@ -506,7 +551,7 @@ static struct inode * __nfs_fhget(struct super_block *sb, struct nfs_fattr *fattr) { struct inode *inode; - int max_count; + int max_count, stale_inode, unhashed = 0; retry: inode = iget(sb, fattr->fileid); @@ -525,27 +570,30 @@ retry: * as the inode may have become a different object. * (We can probably handle modes changes here, too.) */ + stale_inode = inode->i_mode && + ((fattr->mode ^ inode->i_mode) & S_IFMT); + stale_inode |= inode->i_count && inode->i_count == unhashed; max_count = S_ISDIR(fattr->mode) ? 1 : fattr->nlink; - if (inode->i_count > max_count) { -printk("__nfs_fhget: inode %ld busy, i_count=%d, i_nlink=%d\n", -inode->i_ino, inode->i_count, inode->i_nlink); - nfs_free_dentries(inode); - if (inode->i_count > max_count) { -printk("__nfs_fhget: inode %ld still busy, i_count=%d\n", -inode->i_ino, inode->i_count); + if (stale_inode || inode->i_count > max_count + unhashed) { + dprintk("__nfs_fhget: inode %ld busy, i_count=%d, i_nlink=%d\n", + inode->i_ino, inode->i_count, inode->i_nlink); + unhashed = nfs_free_dentries(inode); + if (stale_inode || inode->i_count > max_count + unhashed) { + printk("__nfs_fhget: inode %ld still busy, i_count=%d\n", + inode->i_ino, inode->i_count); if (!list_empty(&inode->i_dentry)) { struct dentry *dentry; dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias); -printk("__nfs_fhget: killing %s/%s filehandle\n", -dentry->d_parent->d_name.name, dentry->d_name.name); - memset(dentry->d_fsdata, 0, + printk("__nfs_fhget: killing %s/%s filehandle\n", + dentry->d_parent->d_name.name, + dentry->d_name.name); + memset(dentry->d_fsdata, 0, sizeof(struct nfs_fh)); - } else - printk("NFS: inode %ld busy, no aliases?\n", - inode->i_ino); - make_bad_inode(inode); + } remove_inode_hash(inode); + nfs_invalidate_inode(inode); + unhashed = 0; } iput(inode); goto retry; @@ -597,10 +645,8 @@ printk("nfs_notify_change: revalidate failed, error=%d\n", error); sattr.gid = attr->ia_gid; sattr.size = (u32) -1; - if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) { + if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) sattr.size = attr->ia_size; - nfs_flush_trunc(inode, sattr.size); - } sattr.mtime.seconds = sattr.mtime.useconds = (u32) -1; if (attr->ia_valid & ATTR_MTIME) { @@ -614,6 +660,10 @@ printk("nfs_notify_change: revalidate failed, error=%d\n", error); sattr.atime.useconds = 0; } + error = nfs_wb_all(inode); + if (error) + goto out; + error = nfs_proc_setattr(NFS_DSERVER(dentry), NFS_FH(dentry), &sattr, &fattr); if (error) @@ -646,6 +696,22 @@ nfs_revalidate(struct dentry *dentry) } /* + * These are probably going to contain hooks for + * allocating and releasing RPC credentials for + * the file. I'll have to think about Tronds patch + * a bit more.. + */ +int nfs_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +int nfs_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +/* * This function is called whenever some part of NFS notices that * the cached attributes have to be refreshed. */ @@ -656,10 +722,6 @@ _nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry) int status = 0; struct nfs_fattr fattr; - /* Don't bother revalidating if we've done it recently */ - if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode)) - goto out; - dfprintk(PAGECACHE, "NFS: revalidating %s/%s, ino=%ld\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino); @@ -668,10 +730,9 @@ _nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry) int error; u32 *fh; struct nfs_fh fhandle; -#ifdef NFS_PARANOIA -printk("nfs_revalidate_inode: %s/%s getattr failed, ino=%ld, error=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status); -#endif + dfprintk(PAGECACHE, "nfs_revalidate_inode: %s/%s getattr failed, ino=%ld, error=%d\n", + dentry->d_parent->d_name.name, + dentry->d_name.name, inode->i_ino, status); if (status != -ESTALE) goto out; /* @@ -679,26 +740,25 @@ dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status); * and find out what the filehandle should be. */ fh = (u32 *) NFS_FH(dentry); - printk("NFS: bad fh %08x%08x%08x%08x%08x%08x%08x%08x\n", + dfprintk(PAGECACHE, "NFS: bad fh %08x%08x%08x%08x%08x%08x%08x%08x\n", fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]); error = nfs_proc_lookup(server, NFS_FH(dentry->d_parent), dentry->d_name.name, &fhandle, &fattr); if (error) { - printk("NFS: lookup failed, error=%d\n", error); + dfprintk(PAGECACHE, "NFS: lookup failed, error=%d\n", error); goto out; } fh = (u32 *) &fhandle; - printk(" %08x%08x%08x%08x%08x%08x%08x%08x\n", + dfprintk(PAGECACHE, " %08x%08x%08x%08x%08x%08x%08x%08x\n", fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]); goto out; } status = nfs_refresh_inode(inode, &fattr); if (status) { -#ifdef NFS_PARANOIA -printk("nfs_revalidate_inode: %s/%s refresh failed, ino=%ld, error=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status); -#endif + dfprintk(PAGECACHE, "nfs_revalidate_inode: %s/%s refresh failed, ino=%ld, error=%d\n", + dentry->d_parent->d_name.name, + dentry->d_name.name, inode->i_ino, status); goto out; } dfprintk(PAGECACHE, "NFS: %s/%s revalidation complete\n", @@ -804,29 +864,18 @@ out_changed: printk("nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n", inode->i_ino, inode->i_mode, fattr->mode); #endif - fattr->mode = inode->i_mode; /* save mode */ - make_bad_inode(inode); - nfs_inval(inode); - inode->i_mode = fattr->mode; /* restore mode */ /* * No need to worry about unhashing the dentry, as the * lookup validation will know that the inode is bad. - * (But we fall through to invalidate the caches.) */ + nfs_invalidate_inode(inode); + goto out; out_invalid: - /* - * Invalidate the local caches - */ #ifdef NFS_DEBUG_VERBOSE printk("nfs_refresh_inode: invalidating %ld pages\n", inode->i_nrpages); #endif - if (!S_ISDIR(inode->i_mode)) - invalidate_inode_pages(inode); - else - nfs_invalidate_dircache(inode); - NFS_CACHEINV(inode); - NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); + nfs_zap_caches(inode); goto out; } diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 1c6a74a71..a4c4e86d5 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -612,6 +612,7 @@ static struct { { NFSERR_FBIG, EFBIG }, { NFSERR_NOSPC, ENOSPC }, { NFSERR_ROFS, EROFS }, + { NFSERR_OPNOTSUPP, EOPNOTSUPP }, { NFSERR_NAMETOOLONG, ENAMETOOLONG }, { NFSERR_NOTEMPTY, ENOTEMPTY }, { NFSERR_DQUOT, EDQUOT }, diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index bf23ad8aa..9194c801f 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -54,13 +54,12 @@ static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen) dfprintk(VFS, "nfs: readlink(%s/%s)\n", dentry->d_parent->d_name.name, dentry->d_name.name); - if (buflen > NFS_MAXPATHLEN) - buflen = NFS_MAXPATHLEN; error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry), - &mem, &res, &len, buflen); + &mem, &res, &len, NFS_MAXPATHLEN); if (! error) { + if (len > buflen) + len = buflen; copy_to_user(buffer, res, len); - put_user('\0', buffer + len); error = len; kfree(mem); } diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 3b30a6a5a..276064c5e 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -50,6 +50,7 @@ #include <linux/malloc.h> #include <linux/swap.h> #include <linux/pagemap.h> +#include <linux/file.h> #include <linux/sunrpc/clnt.h> #include <linux/nfs_fs.h> @@ -237,17 +238,6 @@ update_write_request(struct nfs_wreq *req, unsigned int first, if (rqlast < first || last < rqfirst) return 0; - /* Check the credentials associated with this write request. - * If the buffer is owned by the same user, we can happily - * add our data without risking server permission problems. - * Note that I'm not messing around with RPC root override creds - * here, because they're used by swap requests only which - * always write out full pages. */ - if (!rpcauth_matchcred(&req->wb_task, req->wb_task.tk_cred)) { - dprintk("NFS: update failed (cred mismatch)\n"); - return 0; - } - if (first < rqfirst) rqfirst = first; if (rqlast < last) @@ -271,9 +261,10 @@ free_write_request(struct nfs_wreq * req) * Create and initialize a writeback request */ static inline struct nfs_wreq * -create_write_request(struct dentry *dentry, struct inode *inode, - struct page *page, unsigned int offset, unsigned int bytes) +create_write_request(struct file * file, struct page *page, unsigned int offset, unsigned int bytes) { + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; struct rpc_clnt *clnt = NFS_CLIENT(inode); struct nfs_wreq *wreq; struct rpc_task *task; @@ -298,8 +289,7 @@ create_write_request(struct dentry *dentry, struct inode *inode, goto out_req; /* Put the task on inode's writeback request list. */ - wreq->wb_dentry = dentry; - wreq->wb_inode = inode; + wreq->wb_file = file; wreq->wb_pid = current->pid; wreq->wb_page = page; wreq->wb_offset = offset; @@ -336,7 +326,9 @@ static inline int schedule_write_request(struct nfs_wreq *req, int sync) { struct rpc_task *task = &req->wb_task; - struct inode *inode = req->wb_inode; + struct file *file = req->wb_file; + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; if (NFS_CONGESTED(inode) || nr_write_requests >= NFS_WRITEBACK_MAX) sync = 1; @@ -367,7 +359,10 @@ schedule_write_request(struct nfs_wreq *req, int sync) static int wait_on_write_request(struct nfs_wreq *req) { - struct rpc_clnt *clnt = NFS_CLIENT(req->wb_inode); + struct file *file = req->wb_file; + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + struct rpc_clnt *clnt = NFS_CLIENT(inode); struct wait_queue wait = { current, NULL }; sigset_t oldmask; int retval; @@ -435,7 +430,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig * page and retry the update. */ req = find_write_request(inode, page); - if (req && update_write_request(req, offset, count)) + if (req && req->wb_file == file && update_write_request(req, offset, count)) goto updated; /* @@ -446,7 +441,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig return nfs_writepage_sync(dentry, inode, page, offset, count); /* Create the write request. */ - req = create_write_request(dentry, inode, page, offset, count); + req = create_write_request(file, page, offset, count); if (!req) return -ENOBUFS; @@ -455,7 +450,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig * The IO completion will then free the page and the dentry. */ atomic_inc(&page->count); - dget(dentry); + file->f_count++; /* Schedule request */ synchronous = schedule_write_request(req, sync); @@ -571,24 +566,12 @@ nfs_wb_page(struct inode *inode, struct page *page) } /* - * Write back all pending writes for one user.. + * Write back all pending writes from one file descriptor.. */ int -nfs_wb_pid(struct inode *inode, pid_t pid) +nfs_wb_file(struct inode *inode, struct file *file) { - NFS_WB(inode, req->wb_pid == pid); -} - -/* - * Flush all write requests for truncation: - * Simplification of the comparison has the side-effect of - * causing all writes in an infested page to be waited upon. - */ -int -nfs_flush_trunc(struct inode *inode, unsigned long from) -{ - from &= PAGE_MASK; - NFS_WB(inode, req->wb_page->offset >= from); + NFS_WB(inode, req->wb_file == file); } void @@ -598,16 +581,6 @@ nfs_inval(struct inode *inode) } /* - * Check if a previous write operation returned an error - */ -int -nfs_check_error(struct inode *inode) -{ - /* FIXME! */ - return 0; -} - -/* * The following procedures make up the writeback finite state machinery: * * 1. Try to lock the page if not yet locked by us, @@ -618,7 +591,8 @@ nfs_wback_begin(struct rpc_task *task) { struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata; struct page *page = req->wb_page; - struct dentry *dentry = req->wb_dentry; + struct file *file = req->wb_file; + struct dentry *dentry = file->f_dentry; dprintk("NFS: %4d nfs_wback_begin (%s/%s, status=%d flags=%x)\n", task->tk_pid, dentry->d_parent->d_name.name, @@ -645,13 +619,15 @@ static void nfs_wback_result(struct rpc_task *task) { struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata; - struct inode *inode = req->wb_inode; - struct page *page = req->wb_page; + struct file *file = req->wb_file; + struct page *page = req->wb_page; int status = task->tk_status; + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; dprintk("NFS: %4d nfs_wback_result (%s/%s, status=%d, flags=%x)\n", - task->tk_pid, req->wb_dentry->d_parent->d_name.name, - req->wb_dentry->d_name.name, status, req->wb_flags); + task->tk_pid, dentry->d_parent->d_name.name, + dentry->d_name.name, status, req->wb_flags); /* Set the WRITE_COMPLETE flag, but leave WRITE_INPROGRESS set */ req->wb_flags |= NFS_WRITE_COMPLETE; @@ -659,6 +635,7 @@ nfs_wback_result(struct rpc_task *task) if (status < 0) { req->wb_flags |= NFS_WRITE_INVALIDATE; + file->f_error = status; } else if (!WB_CANCELLED(req)) { struct nfs_fattr *fattr = &req->wb_fattr; /* Update attributes as result of writeback. @@ -696,22 +673,8 @@ nfs_wback_result(struct rpc_task *task) __free_page(page); remove_write_request(&NFS_WRITEBACK(inode), req); nr_write_requests--; - dput(req->wb_dentry); + fput(req->wb_file); wake_up(&req->wb_wait); - - /* - * FIXME! - * - * We should not free the request here if it has pending - * error status on it. We should just leave it around, to - * let the error be collected later. However, the error - * collecting routines are too stupid for that right now, - * so we just drop the error on the floor at this point - * for any async writes. - * - * This should not be a major headache to fix, but I want - * to validate basic operations first. - */ free_write_request(req); } diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index 3e132a75b..5396a784f 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -339,8 +339,10 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp, struct nfsd3_symlinkargs *argp, nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen, argp->tname, argp->tlen, &newfh); - if (nfserr) + if (!nfserr) { + argp->attrs.ia_valid &= ~ATTR_SIZE; nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs); + } fh_put(&argp->ffh); fh_put(&newfh); @@ -362,6 +364,7 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp, struct nfsd3_createargs *argp, SVCFH_INO(&argp->fh), argp->name); + argp->attrs.ia_valid &= ~ATTR_SIZE; nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len, &argp->attrs, S_IFDIR, 0, &resp->fh); fh_put(&argp->fh); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 0a3cc3f8b..6f6b4a733 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -13,6 +13,7 @@ #include <linux/string.h> #include <linux/stat.h> #include <linux/dcache.h> +#include <asm/pgtable.h> #include <linux/sunrpc/svc.h> #include <linux/nfsd/nfsd.h> @@ -235,7 +236,7 @@ retry: /* * Search for a path entry for the specified (dev, inode). */ -struct nfsd_path *get_path_entry(kdev_t dev, ino_t ino) +static struct nfsd_path *get_path_entry(kdev_t dev, ino_t ino) { struct nfsd_path *pe; struct list_head *tmp; @@ -713,7 +714,6 @@ printk("find_dentry_by_ino: getting root dentry for %s\n", kdevname(dev)); dentry = dget(fhe->dentry); goto out; } - /* * Search the path cache ... */ @@ -859,6 +859,34 @@ static struct dentry *nfsd_cached_lookup(struct knfs_fh *fh) return NULL; } +void +expire_all(void) +{ + if (time_after_eq(jiffies, nfsd_next_expire)) { + expire_old(NFSD_FILE_CACHE, 5*HZ); + expire_old(NFSD_DIR_CACHE , 60*HZ); + nfsd_next_expire = jiffies + 5*HZ; + } +} + +/* + * Free cache after unlink/rmdir. + */ +void +expire_by_dentry(struct dentry *dentry) +{ + struct fh_entry *fhe; + + fhe = find_fhe(dentry, NFSD_FILE_CACHE, NULL); + if (fhe) { + expire_fhe(fhe, NFSD_FILE_CACHE); + } + fhe = find_fhe(dentry, NFSD_DIR_CACHE, NULL); + if (fhe) { + expire_fhe(fhe, NFSD_DIR_CACHE); + } +} + /* * The is the basic lookup mechanism for turning an NFS file handle * into a dentry. There are several levels to the search: @@ -997,15 +1025,8 @@ out: add_to_lookup_cache(dentry, fh); } - /* - * Perform any needed housekeeping ... - * N.B. move this into one of the daemons ... - */ - if (time_after_eq(jiffies, nfsd_next_expire)) { - expire_old(NFSD_FILE_CACHE, 5*HZ); - expire_old(NFSD_DIR_CACHE , 60*HZ); - nfsd_next_expire = jiffies + 5*HZ; - } + expire_all(); + return dentry; } @@ -1133,7 +1154,7 @@ dprintk("fh_verify: no root_squashed access.\n"); #ifdef NFSD_PARANOIA if (error) printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, access, error); +dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24)); #endif out: return error; @@ -1260,11 +1281,8 @@ static int nfsd_d_validate(struct dentry *dentry) goto bad_addr; if ((dent_addr & ~align_mask) != dent_addr) goto bad_align; - /* XXX: Should test here, whether the address doesn't belong to - a physical memory hole on sparc32/sparc64. Then it is not - safe to dereference it. On the other side, the previous - use of num_physpages instead of max_mapnr caused the same - to happen, plus some valid addresses could get rejected. -jj */ + if (!kern_addr_valid(dent_addr)) + goto bad_addr; /* * Looks safe enough to dereference ... */ diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 8b115ed3a..7c3e24817 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -382,8 +382,10 @@ nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp, nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen, argp->tname, argp->tlen, &newfh); - if (!nfserr) + if (!nfserr) { + argp->attrs.ia_valid &= ~ATTR_SIZE; nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs); + } fh_put(&argp->ffh); fh_put(&newfh); @@ -407,6 +409,7 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp, struct nfsd_createargs *argp, "nfsd_proc_mkdir: response already verified??\n"); } + argp->attrs.ia_valid &= ~ATTR_SIZE; nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len, &argp->attrs, S_IFDIR, 0, &resp->fh); fh_put(&argp->fh); @@ -515,7 +518,7 @@ struct svc_procedure nfsd_procedures2[18] = { PROC(symlink, symlinkargs, void, none, RC_REPLSTAT), PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF), PROC(rmdir, diropargs, void, none, RC_REPLSTAT), - PROC(readdir, readdirargs, readdirres, none, RC_REPLSTAT), + PROC(readdir, readdirargs, readdirres, none, RC_REPLBUFF), PROC(statfs, fhandle, statfsres, none, RC_NOCACHE), }; diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index ff1608a82..b7fa534e0 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -101,7 +101,7 @@ static void nfsd(struct svc_rqst *rqstp) { struct svc_serv *serv = rqstp->rq_server; - int oldumask, err; + int oldumask, err, first = 0; /* Lock module and set up kernel thread */ MOD_INC_USE_COUNT; @@ -115,8 +115,10 @@ nfsd(struct svc_rqst *rqstp) oldumask = current->fs->umask; /* Set umask to 0. */ current->fs->umask = 0; - if (!nfsd_active++) + if (!nfsd_active++) { nfssvc_boot = xtime; /* record boot time */ + first = 1; + } lockd_up(); /* start lockd */ /* @@ -133,8 +135,14 @@ nfsd(struct svc_rqst *rqstp) * Find a socket with data available and call its * recvfrom routine. */ - while ((err = svc_recv(serv, rqstp, MAX_SCHEDULE_TIMEOUT)) == -EAGAIN) - ; + while ((err = svc_recv(serv, rqstp, + first?5*HZ:MAX_SCHEDULE_TIMEOUT)) == -EAGAIN) { + if (first && 1) { + exp_readlock(); + expire_all(); + exp_unlock(); + } + } if (err < 0) break; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 109ed75ec..4ef61fe45 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -11,7 +11,7 @@ * So if you notice code paths that apparently fail to dput() the * dentry, don't worry--they have been taken care of. * - * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de> + * Copyright (C) 1995-1999 Olaf Kirch <okir@monad.swb.de> */ #include <linux/config.h> @@ -734,7 +734,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, * directories via NFS. */ err = 0; - if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) + if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) err = nfsd_setattr(rqstp, resfhp, iap); out: return err; @@ -959,7 +959,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, goto out_unlock; err = nfserr_perm; - if (IS_IMMUTABLE(dest) /* || IS_APPEND(dest) */ ) + if (IS_IMMUTABLE(dest) || IS_APPEND(dest)) goto out_unlock; if (!dirp->i_op || !dirp->i_op->link) goto out_unlock; @@ -1139,8 +1139,11 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, goto out; } + expire_by_dentry(rdentry); + if (type != S_IFDIR) { /* It's UNLINK */ + err = fh_lock_parent(fhp, rdentry); if (err) goto out; @@ -1155,6 +1158,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, } else { /* It's RMDIR */ /* See comments in fs/namei.c:do_rmdir */ + rdentry->d_count++; nfsd_double_down(&dirp->i_sem, &rdentry->d_inode->i_sem); if (!fhp->fh_pre_mtime) diff --git a/fs/nls/Config.in b/fs/nls/Config.in index bf17009d9..7e591b82a 100644 --- a/fs/nls/Config.in +++ b/fs/nls/Config.in @@ -4,7 +4,7 @@ # msdos and Joliet want NLS if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" \ - -o "$CONFIG_NTFS_FS" != "n" ]; then + -o "$CONFIG_NTFS_FS" != "n" -o "$CONFIG_NCPFS_NLS" = "y" ]; then define_bool CONFIG_NLS y else define_bool CONFIG_NLS n diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile index 8546f0bde..f06e122aa 100644 --- a/fs/ntfs/Makefile +++ b/fs/ntfs/Makefile @@ -3,7 +3,7 @@ O_TARGET := ntfs.o O_OBJS := fs.o sysctl.o support.o util.o inode.o dir.o super.o attr.o M_OBJS := $(O_TARGET) -EXTRA_CFLAGS = -DNTFS_IN_LINUX_KERNEL -DNTFS_VERSION=\"990102\" +EXTRA_CFLAGS = -DNTFS_IN_LINUX_KERNEL -DNTFS_VERSION=\"990411\" include $(TOPDIR)/Rules.make diff --git a/fs/ntfs/attr.c b/fs/ntfs/attr.c index 3de728fb7..3b8277643 100644 --- a/fs/ntfs/attr.c +++ b/fs/ntfs/attr.c @@ -1,9 +1,10 @@ /* * attr.c * - * Copyright (C) 1996-1998 Martin von Löwis + * Copyright (C) 1996-1999 Martin von Löwis * Copyright (C) 1996-1997 Régis Duchesne * Copyright (C) 1998 Joseph Malicki + * Copyright (C) 1999 Steve Dodd */ #include "ntfstypes.h" @@ -11,6 +12,9 @@ #include "attr.h" #include <linux/errno.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif #include "macros.h" #include "support.h" #include "util.h" @@ -18,8 +22,9 @@ #include "inode.h" /* Look if an attribute already exists in the inode, and if not, create it */ -static int -new_attr(ntfs_inode *ino,int type,void *name,int namelen,int *pos, int *found) +int +ntfs_new_attr(ntfs_inode *ino,int type,void *name,int namelen,int *pos, + int *found, int do_search ) { int do_insert=0; int i; @@ -28,15 +33,17 @@ new_attr(ntfs_inode *ino,int type,void *name,int namelen,int *pos, int *found) { int n=min(namelen,ino->attrs[i].namelen); int s=ntfs_uni_strncmp(ino->attrs[i].name,name,n); - /* - * We assume that each attribute can be uniquely - * identified by inode - * number, attribute type and attribute name. - */ - if(ino->attrs[i].type==type && ino->attrs[i].namelen==namelen && !s){ - *found=1; - *pos=i; - return 0; + if( do_search ) { + /* + * We assume that each attribute can be uniquely + * identified by inode + * number, attribute type and attribute name. + */ + if(ino->attrs[i].type==type && ino->attrs[i].namelen==namelen && !s){ + *found=1; + *pos=i; + return 0; + } } /* attributes are ordered by type, then by name */ if(ino->attrs[i].type>type || (ino->attrs[i].type==type && s==1)){ @@ -48,17 +55,21 @@ new_attr(ntfs_inode *ino,int type,void *name,int namelen,int *pos, int *found) /* re-allocate space */ if(ino->attr_count % 8 ==0) { - ntfs_attribute* old=ino->attrs; - ino->attrs = (ntfs_attribute*)ntfs_malloc((ino->attr_count+8)* + ntfs_attribute* new; + new = (ntfs_attribute*)ntfs_malloc((ino->attr_count+8)* sizeof(ntfs_attribute)); - if(old){ - ntfs_memcpy(ino->attrs,old,ino->attr_count*sizeof(ntfs_attribute)); - ntfs_free(old); + if( !new ) + return ENOMEM; + if( ino->attrs ) { + ntfs_memcpy( new, ino->attrs, ino->attr_count*sizeof(ntfs_attribute) ); + ntfs_free( ino->attrs ); } + ino->attrs = new; } if(do_insert) ntfs_memmove(ino->attrs+i+1,ino->attrs+i,(ino->attr_count-i)* sizeof(ntfs_attribute)); + ino->attr_count++; ino->attrs[i].type=type; ino->attrs[i].namelen=namelen; @@ -81,19 +92,21 @@ ntfs_make_attr_resident(ntfs_inode *ino,ntfs_attribute *attr) } /* Store in the inode readable information about a run */ -static void +void ntfs_insert_run(ntfs_attribute *attr,int cnum,ntfs_cluster_t cluster,int len) { /* (re-)allocate space if necessary */ if(attr->d.r.len % 8 == 0) { - ntfs_runlist* old; - old=attr->d.r.runlist; - attr->d.r.runlist=ntfs_malloc((attr->d.r.len+8)*sizeof(ntfs_runlist)); - if(old) { - ntfs_memcpy(attr->d.r.runlist,old,attr->d.r.len + ntfs_runlist* new; + new = ntfs_malloc((attr->d.r.len+8)*sizeof(ntfs_runlist)); + if( !new ) + return; + if( attr->d.r.runlist ) { + ntfs_memcpy(new, attr->d.r.runlist, attr->d.r.len *sizeof(ntfs_runlist)); - ntfs_free(old); + ntfs_free( attr->d.r.runlist ); } + attr->d.r.runlist = new; } if(attr->d.r.len>cnum) ntfs_memmove(attr->d.r.runlist+cnum+1,attr->d.r.runlist+cnum, @@ -103,6 +116,13 @@ ntfs_insert_run(ntfs_attribute *attr,int cnum,ntfs_cluster_t cluster,int len) attr->d.r.len++; } +/* Extends an attribute. Another run will be added if necessary, + * but we try to extend the last run in the runlist first. + * FIXME: what if there isn't enough contiguous space, we don't create + * multiple runs? + * + * *len: the desired new length of the attr (_not_ the amount to extend by) + */ int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, int *len, int flags) { @@ -111,34 +131,46 @@ int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, int *len, int rlen; ntfs_cluster_t cluster; int clen; + if(attr->compressed)return EOPNOTSUPP; if(ino->record_count>1)return EOPNOTSUPP; + if(attr->resident) { error = ntfs_make_attr_nonresident(ino,attr); if(error) return error; } + if( *len <= attr->allocated ) + return 0; /* truely stupid things do sometimes happen */ + rl=attr->d.r.runlist; rlen=attr->d.r.len-1; + if(rlen>=0) cluster=rl[rlen].cluster+rl[rlen].len; else /* no preference for allocation space */ cluster=0; - /* round up to multiple of cluster size */ - clen=(*len+ino->vol->clustersize-1)/ino->vol->clustersize; + + /* calculate the extra space we need, and round up to multiple of cluster + * size to get number of new clusters needed */ + + clen=( (*len - attr->allocated ) + ino->vol->clustersize - 1 ) / + ino->vol->clustersize; if(clen==0) return 0; + /* FIXME: try to allocate smaller pieces */ error=ntfs_allocate_clusters(ino->vol,&cluster,&clen, flags|ALLOC_REQUIRE_SIZE); if(error)return error; attr->allocated += clen*ino->vol->clustersize; - *len=clen*ino->vol->clustersize; + *len = attr->allocated; + /* contiguous chunk */ if(rlen>=0 && cluster==rl[rlen].cluster+rl[rlen].len){ - rl[rlen].len+=clen; + rl[rlen].len += clen; return 0; } ntfs_insert_run(attr,rlen+1,cluster,clen); @@ -208,8 +240,10 @@ int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, int newsize) v=attr->d.data; if(newsize){ attr->d.data=ntfs_malloc(newsize); - if(!attr->d.data) + if(!attr->d.data) { + ntfs_free(v); return ENOMEM; + } if(newsize>oldsize) ntfs_bzero((char*)attr->d.data+oldsize, newsize-oldsize); @@ -231,7 +265,7 @@ int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, int newsize) newlen=i+1; /* free unused clusters in current run, unless sparse */ newcount=count; - if(rl[i].cluster!=-1){ + if(rl[i].cluster!=MAX_CLUSTER_T){ int rounded=newsize-count*clustersize; rounded=(rounded+clustersize-1)/clustersize; error=ntfs_deallocate_clusters(ino->vol,rl[i].cluster+rounded, @@ -243,7 +277,7 @@ int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, int newsize) } /* free all other runs */ for(i++;i<attr->d.r.len;i++) - if(rl[i].cluster!=-1){ + if(rl[i].cluster!=MAX_CLUSTER_T){ error=ntfs_deallocate_clusters(ino->vol,rl[i].cluster,(int)rl[i].len); if(error) return error; /* FIXME: incomplete operation */ @@ -279,12 +313,20 @@ int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data, if(aname){ namelen=strlen(aname); name=ntfs_malloc(2*namelen); + if( !name ) + return ENOMEM; ntfs_ascii2uni(name,aname,namelen); }else{ name=0; namelen=0; } - new_attr(ino,anum,name,namelen,&i,&found); + + error = ntfs_new_attr(ino,anum,name,namelen,&i,&found,1); + if( error ) { + ntfs_free( name ); + return error; + } + if(found){ ntfs_free(name); return EEXIST; @@ -309,6 +351,10 @@ int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data, }else attr->indexed=0; attr->d.data=ntfs_malloc(dsize); + + if( !attr->d.data ) + return ENOMEM; + ntfs_memcpy(attr->d.data,data,dsize); return 0; } @@ -366,6 +412,7 @@ int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata) int namelen; void *data; ntfs_attribute *attr; + int error; type = NTFS_GETU32(attrdata); namelen = NTFS_GETU8(attrdata+9); @@ -376,15 +423,30 @@ int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata) { /* 1 Unicode character fits in 2 bytes */ name=ntfs_malloc(2*namelen); + if( !name ) + return ENOMEM; + ntfs_memcpy(name,attrdata+NTFS_GETU16(attrdata+10),2*namelen); } - new_attr(ino,type,name,namelen,&i,&found); + + error = ntfs_new_attr(ino,type,name,namelen,&i,&found,1); + if( error ) { + if( name ) ntfs_free( name ); + return error; + } + /* We can have in one inode two attributes with type 0x00000030 (File Name) and without name */ if(found && /*FIXME*/type!=ino->vol->at_file_name) { ntfs_process_runs(ino,ino->attrs+i,attrdata); return 0; + } else if( found ) { + /* Don't understand the above, but I know it leaks memory below + as it overwrites a found entry without freeing it. So here we + call ntfs_new_attr again but this time ask it to always allocate a + new entry */ + ntfs_new_attr(ino,type,name,namelen,&i,&found,0); } attr=ino->attrs+i; attr->resident=NTFS_GETU8(attrdata+8)==0; @@ -395,6 +457,8 @@ int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata) attr->size=NTFS_GETU16(attrdata+0x10); data=attrdata+NTFS_GETU16(attrdata+0x14); attr->d.data = (void*)ntfs_malloc(attr->size); + if( !attr->d.data ) + return ENOMEM; ntfs_memcpy(attr->d.data,data,attr->size); attr->indexed=NTFS_GETU16(attrdata+0x16); }else{ @@ -468,7 +532,7 @@ int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset, copied=0; while(l){ chunk=0; - if(cluster==-1){ + if(cluster==MAX_CLUSTER_T){ /* sparse cluster */ int l1; if((len-(s_vcn-vcn)) & 15) @@ -503,9 +567,9 @@ int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset, } got+=l1; comp1+=l1*clustersize; - }while(cluster!=-1 && got<16); /* until empty run */ + }while(cluster!=MAX_CLUSTER_T && got<16); /* until empty run */ chunk=16*clustersize; - if(cluster!=-1 || got==16) + if(cluster!=MAX_CLUSTER_T || got==16) /* uncompressible */ comp1=comp; else{ diff --git a/fs/ntfs/attr.h b/fs/ntfs/attr.h index 6ba6f5b27..1be5f8d0e 100644 --- a/fs/ntfs/attr.h +++ b/fs/ntfs/attr.h @@ -18,3 +18,7 @@ int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data, int ntfs_read_zero(ntfs_io *dest,int size); int ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr); int ntfs_attr_allnonresident(ntfs_inode *ino); +int ntfs_new_attr( ntfs_inode *ino, int type, void *name, int namelen, + int *pos, int *found, int do_search ); +void ntfs_insert_run( ntfs_attribute *attr, int cnum, ntfs_cluster_t cluster, + int len ); diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index ff18aa15c..6d92bce11 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -1,7 +1,9 @@ /* * dir.c * - * Copyright (C) 1995-1997 Martin von Löwis + * Copyright (C) 1995-1997, 1999 Martin von Löwis + * Copyright (C) 1999 Steve Dodd + * Copyright (C) 1999 Joseph Malicki */ #include "ntfstypes.h" @@ -67,7 +69,7 @@ static ntfs_u64 ntfs_push(ntfs_u64 stack,int i) if(i<55)return (stack<<8)|((i-23)<<3)|3; if(i<120)return (stack<<10)|((i-55)<<4)|7; ntfs_error("Too many entries\n"); - return 0xFFFFFFFFFFFFFFFF; + return ~((ntfs_u64)0); } #if 0 @@ -166,6 +168,8 @@ static int ntfs_allocate_index_block(ntfs_iterate_s *walk) int nr_fix = s1/vol->blocksize+1; int hsize; char *record=ntfs_malloc(s1); + if( !record ) + return ENOMEM; ntfs_bzero(record,s1); /* magic */ ntfs_memcpy(record,"INDX",4); @@ -356,8 +360,10 @@ static int ntfs_dir_insert(ntfs_iterate_s *walk, char *start, char* entry) if(do_split){ error=ntfs_split_record(walk,start,blocksize,usedsize); ntfs_free(start); - }else - ntfs_index_writeback(walk,start,walk->block,usedsize); + }else{ + error=ntfs_index_writeback(walk,start,walk->block,usedsize); + if(error)return error; + } return 0; } @@ -395,8 +401,9 @@ ntfs_split_indexroot(ntfs_inode *ino) goto out; } index = ntfs_malloc(ino->vol->index_recordsize); - if(!index) - goto out; + if(!index) { + error = ENOMEM; goto out; + } walk.dir = ino; walk.block = -1; walk.result = walk.new_entry = 0; @@ -462,12 +469,12 @@ static int ntfs_my_strcmp(ntfs_iterate_s *walk, const unsigned char *entry) ntfs_u16* name=(ntfs_u16*)(entry+0x52); ntfs_volume *vol=walk->dir->vol; for(i=0;i<lu && i<walk->namelen;i++) - if(ntfs_my_toupper(vol,name[i])!=ntfs_my_toupper(vol,walk->name[i])) + if(ntfs_my_toupper(vol,NTFS_GETU16(name+i))!=ntfs_my_toupper(vol,NTFS_GETU16(walk->name+i))) break; if(i==lu && i==walk->namelen)return 0; if(i==lu)return 1; if(i==walk->namelen)return -1; - if(ntfs_my_toupper(vol,name[i])<ntfs_my_toupper(vol,walk->name[i]))return 1; + if(ntfs_my_toupper(vol,NTFS_GETU16(name+i))<ntfs_my_toupper(vol,NTFS_GETU16(walk->name+i)))return 1; return -1; } @@ -485,6 +492,9 @@ static int ntfs_getdir_record(ntfs_iterate_s *walk, int block) int oldblock; ntfs_io io; + if( !record ) + return ENOMEM; + io.fn_put=ntfs_put; io.param=record; io.size=length; @@ -607,7 +617,6 @@ ntfs_getdir_iterate_byposition(ntfs_iterate_s *walk,char* start,char *entry) static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry) { int length; - int retval=0; int cmp; if(walk->type==BY_POSITION) @@ -647,7 +656,7 @@ static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry) } entry+=length; }while(1); - return retval; + return 0; } /* Tree walking is done using position numbers. The following numbers have @@ -683,6 +692,9 @@ int ntfs_getdir(ntfs_iterate_s* walk) char *root=ntfs_malloc(length); ntfs_io io; + if( !root ) + return ENOMEM; + io.fn_put=ntfs_put; io.param=root; io.size=length; @@ -747,6 +759,8 @@ int ntfs_getdir_unsorted(ntfs_inode *ino,ntfs_u32 *p_high,ntfs_u32* p_low, /* are we still in the index root */ if(*p_high==0){ buf=ntfs_malloc(length=vol->mft_recordsize); + if( !buf ) + return ENOMEM; io.fn_put=ntfs_put; io.param=buf; io.size=length; @@ -762,6 +776,8 @@ int ntfs_getdir_unsorted(ntfs_inode *ino,ntfs_u32 *p_high,ntfs_u32* p_low, }else{ /* we are in an index record */ length=ino->u.index.recordsize; buf=ntfs_malloc(length); + if( !buf ) + return ENOMEM; io.fn_put=ntfs_put; io.param=buf; io.size=length; @@ -821,6 +837,9 @@ int ntfs_getdir_unsorted(ntfs_inode *ino,ntfs_u32 *p_high,ntfs_u32* p_low, return 0; } buf=ntfs_malloc(length=attr->size); + if( !buf ) + return ENOMEM; + io.param=buf; io.size=length; error=ntfs_read_attr(ino,vol->at_bitmap,I30,0,&io); @@ -920,8 +939,8 @@ int ntfs_dir_add1(ntfs_inode *dir,const char* name,int namelen,ntfs_inode *ino) /* Fills out and creates an INDEX_ROOT attribute. */ -static int -add_index_root (ntfs_inode *ino, int type) +int +ntfs_add_index_root (ntfs_inode *ino, int type) { ntfs_attribute *da; ntfs_u8 data[0x30]; /* 0x20 header, 0x10 last entry */ @@ -959,7 +978,7 @@ ntfs_mkdir(ntfs_inode* dir,const char* name,int namelen, ntfs_inode *result) error = ntfs_alloc_inode(dir, result, name, namelen, NTFS_AFLAG_DIR); if(error) goto out; - error = add_index_root(result, 0x30); + error = ntfs_add_index_root(result, 0x30); if (error) goto out; /* Set directory bit */ diff --git a/fs/ntfs/dir.h b/fs/ntfs/dir.h index 9c86e6063..db6dd0979 100644 --- a/fs/ntfs/dir.h +++ b/fs/ntfs/dir.h @@ -39,3 +39,4 @@ int ntfs_check_index_record(ntfs_inode *ino, char *record); int ntfs_getdir_byposition(ntfs_iterate_s *walk); int ntfs_mkdir(ntfs_inode* dir,const char* name,int namelen, ntfs_inode *ino); int ntfs_split_indexroot(ntfs_inode *ino); +int ntfs_add_index_root( ntfs_inode *ino, int type ); diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c index 016f45a06..f34de38d3 100644 --- a/fs/ntfs/fs.c +++ b/fs/ntfs/fs.c @@ -2,7 +2,7 @@ * fs.c * NTFS driver for Linux 2.1 * - * Copyright (C) 1995-1997 Martin von Löwis + * Copyright (C) 1995-1997, 1999 Martin von Löwis * Copyright (C) 1996 Richard Russon * Copyright (C) 1996-1997 Régis Duchesne */ @@ -215,7 +215,6 @@ static int ntfs_readdir(struct file* filp, void *dirent, filldir_t filldir) ntfs_debug(DEBUG_OTHER, "ntfs_readdir ino %x mode %x\n", (unsigned)dir->i_ino,(unsigned int)dir->i_mode); - if(!dir || (dir->i_ino==0) || !S_ISDIR(dir->i_mode))return -EBADF; ntfs_debug(DEBUG_OTHER, "readdir: Looking for file %x dircount %d\n", (unsigned)filp->f_pos,dir->i_count); @@ -373,7 +372,7 @@ static int parse_options(ntfs_volume* vol,char *opt) return 0; } -static int ntfs_lookup(struct inode *dir, struct dentry *d) +static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *d) { struct inode *res=0; char *item=0; @@ -385,8 +384,10 @@ static int ntfs_lookup(struct inode *dir, struct dentry *d) error=ntfs_decodeuni(NTFS_INO2VOL(dir),(char*)d->d_name.name, d->d_name.len,&walk.name,&walk.namelen); if(error) - return error; + return ERR_PTR(-error); item=ntfs_malloc(ITEM_SIZE); + if( !item ) + return ERR_PTR(-ENOMEM); /* ntfs_getdir will place the directory entry into item, and the first long long is the MFT record number */ walk.type=BY_NAME; @@ -400,7 +401,7 @@ static int ntfs_lookup(struct inode *dir, struct dentry *d) ntfs_free(item); ntfs_free(walk.name); /* Always return success, the dcache will handle negative entries. */ - return 0; + return NULL; } static struct file_operations ntfs_file_operations_nommap = { @@ -505,6 +506,7 @@ ntfs_create(struct inode* dir,struct dentry *d,int mode) #endif r->i_mode &= ~vol->umask; + insert_inode_hash(r); d_instantiate(d,r); return 0; fail: @@ -567,6 +569,7 @@ _linux_ntfs_mkdir(struct inode *dir, struct dentry* d, int mode) #endif r->i_mode &= ~vol->umask; + insert_inode_hash(r); d_instantiate(d, r); error = 0; out: @@ -581,8 +584,6 @@ ntfs_bmap(struct inode *ino,int block) int ret=ntfs_vcn_to_lcn(NTFS_LINO2NINO(ino),block); ntfs_debug(DEBUG_OTHER, "bmap of %lx,block %x is %x\n", ino->i_ino,block,ret); - ntfs_error("bmap of %lx,block %x is %x\n", ino->i_ino,block,ret); - ntfs_error("super %x\n", ino->i_sb->s_blocksize); return (ret==-1) ? 0:ret; } @@ -707,6 +708,7 @@ static void ntfs_read_inode(struct inode* inode) #ifdef NTFS_IN_LINUX_KERNEL ino=&inode->u.ntfs_i; #else + /* FIXME: check for ntfs_malloc failure */ ino=(ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode)); inode->u.generic_ip=ino; #endif @@ -816,6 +818,7 @@ static int ntfs_statfs(struct super_block *sb, struct statfs *sf, int bufsize) struct statfs fs; struct inode *mft; ntfs_volume *vol; + int error; ntfs_debug(DEBUG_OTHER, "ntfs_statfs\n"); vol=NTFS_SB2VOL(sb); @@ -823,7 +826,9 @@ static int ntfs_statfs(struct super_block *sb, struct statfs *sf, int bufsize) fs.f_type=NTFS_SUPER_MAGIC; fs.f_bsize=vol->clustersize; - fs.f_blocks=ntfs_get_volumesize(NTFS_SB2VOL(sb)); + error = ntfs_get_volumesize( NTFS_SB2VOL( sb ), &fs.f_blocks ); + if( error ) + return -error; fs.f_bfree=ntfs_get_free_cluster_count(vol->bitmap); fs.f_bavail=fs.f_bfree; diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index a70205e2f..7fcc86754 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -5,13 +5,18 @@ * Copyright (C) 1996 Albert D. Cahalan * Copyright (C) 1996-1997 Régis Duchesne * Copyright (C) 1998 Joseph Malicki + * Copyright (C) 1999 Steve Dodd */ #include "ntfstypes.h" +#include "ntfsendian.h" #include "struct.h" #include "inode.h" #include <linux/errno.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif #include "macros.h" #include "attr.h" #include "super.h" @@ -30,8 +35,8 @@ typedef struct { ntfs_mft_record* records; } ntfs_disk_inode; -static void -fill_mft_header(ntfs_u8*mft,int record_size,int blocksize, +void +ntfs_fill_mft_header(ntfs_u8*mft,int record_size,int blocksize, int sequence_number) { int fixup_count = record_size / blocksize + 1; @@ -98,7 +103,7 @@ ntfs_extend_mft(ntfs_volume *vol) if(mdata->allocated<mdata->size+vol->mft_recordsize){ size=ntfs_get_free_cluster_count(vol->bitmap)*vol->clustersize; block=vol->mft_recordsize; - size=max(size/1000,block); + size=max(size/1000,mdata->size+vol->mft_recordsize); size=((size+block-1)/block)*block; /* require this to be a single chunk */ error=ntfs_extend_attr(vol->mft_ino,mdata,&size, @@ -141,14 +146,18 @@ ntfs_extend_mft(ntfs_volume *vol) /* now fill in the MFT header for the new block */ buf=ntfs_calloc(vol->mft_recordsize); if(!buf)return ENOMEM; - fill_mft_header(buf,vol->mft_recordsize,vol->blocksize,0); + ntfs_fill_mft_header(buf,vol->mft_recordsize,vol->blocksize,0); ntfs_insert_fixups(buf,vol->blocksize); io.param=buf; io.size=vol->mft_recordsize; + io.fn_put = ntfs_put; + io.fn_get = ntfs_get; error=ntfs_write_attr(vol->mft_ino,vol->at_data,0, (rcount-1)*vol->mft_recordsize,&io); if(error)return error; if(io.size!=vol->mft_recordsize)return EIO; + error=ntfs_update_inode(vol->mft_ino); + if(error)return error; return 0; } @@ -165,13 +174,16 @@ void ntfs_insert_mft_attributes(ntfs_inode* ino,char *mft,int mftno) /* (re-)allocate space if necessary */ if(ino->record_count % 8==0) { - int *old=ino->records; - ino->records=ntfs_malloc((ino->record_count+8)*sizeof(int)); - if(old) { + int *new; + new = ntfs_malloc((ino->record_count+8)*sizeof(int)); + if( !new ) + return; + if( ino->records ) { for(i=0;i<ino->record_count;i++) - ino->records[i]=old[i]; - ntfs_free(old); + new[i] = ino->records[i]; + ntfs_free( ino->records ); } + ino->records = new; } ino->records[ino->record_count]=mftno; ino->record_count++; @@ -179,8 +191,10 @@ void ntfs_insert_mft_attributes(ntfs_inode* ino,char *mft,int mftno) do{ type=NTFS_GETU32(it); len=NTFS_GETU32(it+4); - if(type!=-1) + if(type!=-1) { + /* FIXME: check ntfs_insert_attribute for failure (e.g. no mem)? */ ntfs_insert_attribute(ino,it); + } it+=len; }while(type!=-1); /* attribute list ends with type -1 */ } @@ -195,6 +209,8 @@ static int parse_attributes(ntfs_inode *ino, ntfs_u8 *alist, int *plen) int last_mft=-1; int len=*plen; mft=ntfs_malloc(ino->vol->mft_recordsize); + if( !mft ) + return ENOMEM; while(len>8) { l=NTFS_GETU16(alist+4); @@ -239,6 +255,8 @@ static void ntfs_load_attributes(ntfs_inode* ino) return; } buf=ntfs_malloc(1024); + if( !buf ) + return; delta=0; for(offset=0;datasize;datasize-=len) { @@ -271,6 +289,8 @@ int ntfs_init_inode(ntfs_inode *ino,ntfs_volume *vol,int inum) ino->i_number=inum; ino->vol=vol; ino->attr=buf=ntfs_malloc(vol->mft_recordsize); + if( !buf ) + return ENOMEM; error=ntfs_read_mft_record(vol,inum,ino->attr); if(error){ ntfs_debug(DEBUG_OTHER, "init inode: %x failed\n",inum); @@ -441,7 +461,7 @@ int ntfs_decompress_run(unsigned char **data, int *length, ntfs_cluster_t *clust int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, int offset, ntfs_io *dest) { - int datasize,rnum; + int rnum; ntfs_cluster_t cluster,s_cluster,vcn,len; int l,chunk,copied; int s_vcn; @@ -449,26 +469,39 @@ int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, int offset, int error; clustersize=ino->vol->clustersize; - datasize=attr->size; l=dest->size; if(l==0) return 0; if(dest->do_read) { - if(offset>=datasize){ + /* if read _starts_ beyond end of stream, return nothing */ + if(offset>=attr->size){ dest->size=0; return 0; } - if(offset+l>=datasize) - l=dest->size=datasize-offset; - }else { /* fixed by CSA: if writing beyond end, extend attribute */ - if (offset+l>datasize) { + + /* if read _extends_ beyond end of stream, return as much + initialised data as we have */ + if(offset+l>=attr->size) + l=dest->size=attr->size-offset; + + }else { + /* fixed by CSA: if writing beyond end, extend attribute */ + + /* if write extends beyond _allocated_ size, extend attrib */ + if (offset+l>attr->allocated) { error=ntfs_resize_attr(ino,attr,offset+l); if(error) return error; } - if (offset+l > attr->initialized) + + /* the amount of initialised data has increased; update */ + /* FIXME: shouldn't we zero-out the section between the old + initialised length and the write start? */ + if (offset+l > attr->initialized) { attr->initialized = offset+l; + attr->size = offset+l; + } } if(attr->resident) { @@ -565,7 +598,6 @@ int ntfs_vcn_to_lcn(ntfs_inode *ino,int vcn) { int rnum; ntfs_attribute *data; - ntfs_error("bmap %x\n",vcn); data=ntfs_find_attr(ino,ino->vol->at_data,0); /* It's hard to give an error code */ if(!data)return -1; @@ -583,7 +615,6 @@ int ntfs_vcn_to_lcn(ntfs_inode *ino,int vcn) vcn>data->d.r.runlist[rnum].len;rnum++) vcn-=data->d.r.runlist[rnum].len; - ntfs_error("result %x\n",data->d.r.runlist[rnum].cluster+vcn); return data->d.r.runlist[rnum].cluster+vcn; } @@ -655,7 +686,7 @@ layout_runs(ntfs_attribute *attr,char* rec,int* offs,int size) *(rec+offset)|=coffs++; - if(rl[i].cluster==0) /*compressed run*/ + if(rl[i].cluster==MAX_CLUSTER_T) /*compressed run*/ /*nothing*/; else if(rclus>-0x80 && rclus<0x7F){ *(rec+offset)|=0x10; @@ -768,7 +799,8 @@ layout_attr(ntfs_attribute* attr,char*buf, int size,int *psize) NTFS_PUTU16(buf+0xA,asize); ntfs_memcpy(buf+asize,attr->name,2*attr->namelen); asize+=2*attr->namelen; - asize=(asize+7) & ~7; + /* SRD: you whaaa? + asize=(asize+7) & ~7;*/ } /* asize points at the beginning of the data */ NTFS_PUTU16(buf+0x20,asize); @@ -841,7 +873,12 @@ layout_inode(ntfs_inode *ino,ntfs_disk_inode *store) if(error) return error; } - next=(next+7) & ~7; /* align to DWORD */ + /* SRD: umm.. + next=(next+7) & ~7; */ + /* is this setting the length? if so maybe we could get + away with rounding up so long as we set the length first.. + ..except, is the length the only way to get to the next attr? + */ NTFS_PUTU16(rec+offset+4,next-offset); offset=next; #endif @@ -1038,7 +1075,8 @@ new_inode (ntfs_volume* vol,int* result) data=ntfs_find_attr(vol->mft_ino,vol->at_data,0); length=data->size/vol->mft_recordsize; - for (byte = 3; 8*byte < length; byte++) + /* SRD: start at byte 0: bits for system files _are_ already set in bitmap */ + for (byte = 0; 8*byte < length; byte++) { value = buffer[byte]; if(value==0xFF) @@ -1064,7 +1102,7 @@ add_mft_header (ntfs_inode *ino) mft=ino->attr; ntfs_bzero(mft, vol->mft_recordsize); - fill_mft_header(mft,vol->mft_recordsize,vol->blocksize, + ntfs_fill_mft_header(mft,vol->mft_recordsize,vol->blocksize, ino->sequence_number); return 0; } @@ -1108,6 +1146,8 @@ add_filename (ntfs_inode* ino, ntfs_inode* dir, /* work out the size */ size = 0x42 + 2 * length; data = ntfs_malloc(size); + if( !data ) + return ENOMEM; ntfs_bzero(data,size); /* search for a position */ @@ -1191,7 +1231,7 @@ int ntfs_alloc_inode (ntfs_inode *dir, ntfs_inode *result, { ntfs_io io; int error; - ntfs_u8 buffer[1]; + ntfs_u8 buffer[2]; ntfs_volume* vol=dir->vol; int byte,bit; @@ -1230,7 +1270,7 @@ int ntfs_alloc_inode (ntfs_inode *dir, ntfs_inode *result, */ /* get the sequence number */ io.param = buffer; - io.size = 0x10; + io.size = 2; error = ntfs_read_attr(vol->mft_ino, vol->at_data, 0, result->i_number*vol->mft_recordsize+0x10,&io); if(error) @@ -1238,10 +1278,17 @@ int ntfs_alloc_inode (ntfs_inode *dir, ntfs_inode *result, result->sequence_number=NTFS_GETU16(buffer)+1; result->vol=vol; result->attr=ntfs_malloc(vol->mft_recordsize); + if( !result->attr ) + return ENOMEM; result->attr_count=0; result->attrs=0; result->record_count=1; result->records=ntfs_malloc(8*sizeof(int)); + if( !result->records ) { + ntfs_free( result->attr ); + result->attr = 0; + return ENOMEM; + } result->records[0]=result->i_number; error=add_mft_header(result); if(error) diff --git a/fs/ntfs/inode.h b/fs/ntfs/inode.h index 606944211..b3627f8cf 100644 --- a/fs/ntfs/inode.h +++ b/fs/ntfs/inode.h @@ -26,3 +26,5 @@ int ntfs_allocate_attr_number(ntfs_inode *ino, int *result); int ntfs_decompress_run(unsigned char **data, int *length, ntfs_cluster_t *cluster, int *ctype); void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l); +void ntfs_fill_mft_header( ntfs_u8 *mft, int recordsize, int blocksize, + int sequence_number ); diff --git a/fs/ntfs/ntfsendian.h b/fs/ntfs/ntfsendian.h index 32d382d57..fc861bc70 100644 --- a/fs/ntfs/ntfsendian.h +++ b/fs/ntfs/ntfsendian.h @@ -1,8 +1,9 @@ /* * ntfsendian.h * - * Copyright (C) 1998 Martin von Löwis + * Copyright (C) 1998, 1999 Martin von Löwis * Copyright (C) 1998 Joseph Malicki + * Copyright (C) 1999 Werner Seiler */ #ifdef __linux__ @@ -36,9 +37,9 @@ #ifdef __BIG_ENDIAN /* We hope its big-endian, not PDP-endian :) */ -#define CPU_TO_LE16(a) ((((a)&0xF) << 8)|((a) >> 8)) -#define CPU_TO_LE32(a) ((((a) & 0xF) << 24) | (((a) & 0xF0) << 8) | \ - (((a) & 0xF00) >> 8) | ((a) >> 24)) +#define CPU_TO_LE16(a) ((((a)&0xFF) << 8)|((a) >> 8)) +#define CPU_TO_LE32(a) ((((a) & 0xFF) << 24) | (((a) & 0xFF00) << 8) | \ + (((a) & 0xFF0000) >> 8) | ((a) >> 24)) #define CPU_TO_LE64(a) ((CPU_TO_LE32(a)<<32)|CPU_TO_LE32((a)>>32) #define LE16_TO_CPU(a) CPU_TO_LE16(a) @@ -55,7 +56,7 @@ #define NTFS_GETU8(p) (*(ntfs_u8*)(p)) #define NTFS_GETU16(p) ((ntfs_u16)LE16_TO_CPU(*(ntfs_u16*)(p))) -#define NTFS_GETU24(p) (NTFS_GETU32(p) & 0xFFFFFF) +#define NTFS_GETU24(p) ((ntfs_u32)NTFS_GETU16(p) | ((ntfs_u32)NTFS_GETU8(((char*)(p))+2)<<16)) #define NTFS_GETU32(p) ((ntfs_u32)LE32_TO_CPU(*(ntfs_u32*)(p))) #define NTFS_GETU40(p) ((ntfs_u64)NTFS_GETU32(p)|(((ntfs_u64)NTFS_GETU8(((char*)(p))+4))<<32)) #define NTFS_GETU48(p) ((ntfs_u64)NTFS_GETU32(p)|(((ntfs_u64)NTFS_GETU16(((char*)(p))+4))<<32)) @@ -75,9 +76,9 @@ #define NTFS_GETS16(p) ((ntfs_s16)LE16_TO_CPU(*(short*)(p))) #define NTFS_GETS24(p) (NTFS_GETU24(p) < 0x800000 ? (int)NTFS_GETU24(p) : (int)(NTFS_GETU24(p) - 0x1000000)) #define NTFS_GETS32(p) ((ntfs_s32)LE32_TO_CPU(*(int*)(p))) -#define NTFS_GETS40(p) (((ntfs_s64)NTFS_GETS32(p)) | (((ntfs_s64)NTFS_GETS8(((char*)(p))+4)) << 32)) -#define NTFS_GETS48(p) (((ntfs_s64)NTFS_GETS32(p)) | (((ntfs_s64)NTFS_GETS16(((char*)(p))+4)) << 32)) -#define NTFS_GETS56(p) (((ntfs_s64)NTFS_GETS32(p)) | (((ntfs_s64)NTFS_GETS24(((char*)(p))+4)) << 32)) +#define NTFS_GETS40(p) (((ntfs_s64)NTFS_GETU32(p)) | (((ntfs_s64)NTFS_GETS8(((char*)(p))+4)) << 32)) +#define NTFS_GETS48(p) (((ntfs_s64)NTFS_GETU32(p)) | (((ntfs_s64)NTFS_GETS16(((char*)(p))+4)) << 32)) +#define NTFS_GETS56(p) (((ntfs_s64)NTFS_GETU32(p)) | (((ntfs_s64)NTFS_GETS24(((char*)(p))+4)) << 32)) #define NTFS_GETS64(p) ((ntfs_s64)NTFS_GETU64(p)) #define NTFS_PUTS8(p,v) NTFS_PUTU8(p,v) diff --git a/fs/ntfs/ntfstypes.h b/fs/ntfs/ntfstypes.h index e88a4169b..075a60806 100644 --- a/fs/ntfs/ntfstypes.h +++ b/fs/ntfs/ntfstypes.h @@ -1,12 +1,12 @@ /* - * types.h + * ntfstypes.h * This file defines four things: * - generic platform independent fixed-size types (e.g. ntfs_u32) * - specific fixed-size types (e.g. ntfs_offset_t) * - macros that read and write those types from and to byte arrays * - types derived from OS specific ones * - * Copyright (C) 1996,1998 Martin von Löwis + * Copyright (C) 1996,1998, 1999 Martin von Löwis */ #ifdef NTFS_IN_LINUX_KERNEL @@ -54,6 +54,10 @@ typedef u64 ntfs_time64_t; typedef u32 ntfs_cluster_t; #endif +#ifndef MAX_CLUSTER_T +#define MAX_CLUSTER_T (~((ntfs_cluster_t)0)) +#endif + /* architecture independent macros */ /* PUTU32 would not clear all bytes */ diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 797a60791..51f14e72b 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1,8 +1,9 @@ /* * super.c * - * Copyright (C) 1995-1997 Martin von Löwis + * Copyright (C) 1995-1997, 1999 Martin von Löwis * Copyright (C) 1996-1997 Régis Duchesne + * Copyright (C) 1999 Steve Dodd */ #include "ntfstypes.h" @@ -113,12 +114,14 @@ ntfs_init_upcase(ntfs_inode *upcase) ntfs_io io; #define UPCASE_LENGTH 256 upcase->vol->upcase = ntfs_malloc(2*UPCASE_LENGTH); - upcase->vol->upcase_length = UPCASE_LENGTH; + if( !upcase->vol->upcase ) + return; io.fn_put=ntfs_put; io.fn_get=0; io.param=(char*)upcase->vol->upcase; io.size=2*UPCASE_LENGTH; ntfs_read_attr(upcase,upcase->vol->at_data,0,0,&io); + upcase->vol->upcase_length = io.size; } static int @@ -246,11 +249,22 @@ int ntfs_release_volume(ntfs_volume *vol) return 0; } -int ntfs_get_volumesize(ntfs_volume *vol) +/* + * Writes the volume size into vol_size. Returns 0 if successful + * or error. + */ +int ntfs_get_volumesize(ntfs_volume *vol, long *vol_size ) { ntfs_io io; - char *cluster0=ntfs_malloc(vol->clustersize); ntfs_u64 size; + char *cluster0; + + if( !vol_size ) + return EFAULT; + + cluster0=ntfs_malloc(vol->clustersize); + if( !cluster0 ) + return ENOMEM; io.fn_put=ntfs_put; io.fn_get=ntfs_get; @@ -262,7 +276,8 @@ int ntfs_get_volumesize(ntfs_volume *vol) ntfs_free(cluster0); /* FIXME: more than 2**32 cluster */ /* FIXME: gcc will emit udivdi3 if we don't truncate it */ - return ((unsigned int)size)/vol->clusterfactor; + *vol_size = ((unsigned long)size)/vol->clusterfactor; + return 0; } static int nc[16]={4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0}; @@ -337,7 +352,7 @@ search_bits(unsigned char* bits,ntfs_cluster_t *loc,int *cnt,int l) int start,stop=0,in=0; /* special case searching for a single block */ if(*cnt==1){ - while(l && *cnt==0xFF){ + while(l && *bits==0xFF){ bits++; *loc+=8; l--; @@ -398,7 +413,7 @@ ntfs_set_bitrange(ntfs_inode* bitmap,ntfs_cluster_t loc,int cnt,int bit) io.fn_put=ntfs_put; io.fn_get=ntfs_get; - bsize=(cnt+(loc & 7)+7) & ~7; /* round up to multiple of 8*/ + bsize=(cnt+(loc & 7)+7) >> 3; /* round up to multiple of 8*/ bits=ntfs_malloc(bsize); io.param=bits; io.size=bsize; @@ -418,7 +433,7 @@ ntfs_set_bitrange(ntfs_inode* bitmap,ntfs_cluster_t loc,int cnt,int bit) else *it &= ~(1<<(locit%8)); cnt--;locit++; - if(locit%8==7) + if(locit%8==0) it++; } while(cnt>8){ /*process full bytes */ @@ -456,10 +471,13 @@ ntfs_search_bits(ntfs_inode* bitmap, ntfs_cluster_t *location, int *count, int f unsigned char *bits; ntfs_io io; int error=0,found=0; - int loc,cnt,bloc=-1,bcnt=0; + int cnt,bloc=-1,bcnt=0; int start; + ntfs_cluster_t loc; bits=ntfs_malloc(2048); + if( !bits ) + return ENOMEM; io.fn_put=ntfs_put; io.fn_get=ntfs_get; io.param=bits; diff --git a/fs/ntfs/super.h b/fs/ntfs/super.h index f90490136..b74772143 100644 --- a/fs/ntfs/super.h +++ b/fs/ntfs/super.h @@ -10,7 +10,7 @@ #define ALLOC_REQUIRE_SIZE 2 int ntfs_get_free_cluster_count(ntfs_inode *bitmap); -int ntfs_get_volumesize(ntfs_volume *vol); +int ntfs_get_volumesize(ntfs_volume *vol, long *vol_size ); int ntfs_init_volume(ntfs_volume *vol,char *boot); int ntfs_load_special_files(ntfs_volume *vol); int ntfs_release_volume(ntfs_volume *vol); diff --git a/fs/ntfs/support.c b/fs/ntfs/support.c index e0b79c8de..6ee3b332b 100644 --- a/fs/ntfs/support.c +++ b/fs/ntfs/support.c @@ -246,6 +246,10 @@ int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out, if(!(vol->nct & nct_uni_xlate))goto inval; /* realloc */ buf=ntfs_malloc(*out_len+3); + if( !buf ) { + ntfs_free( result ); + return ENOMEM; + } memcpy(buf,result,o); ntfs_free(result); result=buf; @@ -22,10 +22,11 @@ asmlinkage int sys_statfs(const char * path, struct statfs * buf) error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { struct inode * inode = dentry->d_inode; + struct super_block * sb = inode->i_sb; - error = -ENOSYS; - if (inode->i_sb->s_op->statfs) - error = inode->i_sb->s_op->statfs(inode->i_sb, buf, sizeof(struct statfs)); + error = -ENODEV; + if (sb && sb->s_op && sb->s_op->statfs) + error = sb->s_op->statfs(sb, buf, sizeof(struct statfs)); dput(dentry); } @@ -52,10 +53,8 @@ asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf) if (!(inode = dentry->d_inode)) goto out_putf; error = -ENODEV; - if (!(sb = inode->i_sb)) - goto out_putf; - error = -ENOSYS; - if (sb->s_op->statfs) + sb = inode->i_sb; + if (sb && sb->s_op && sb->s_op->statfs) error = sb->s_op->statfs(sb, buf, sizeof(struct statfs)); out_putf: fput(file); @@ -70,6 +69,10 @@ int do_truncate(struct dentry *dentry, unsigned long length) int error; struct iattr newattrs; + /* Not pretty: "inode->i_size" shouldn't really be "off_t". But it is. */ + if ((off_t) length < 0) + return -EINVAL; + down(&inode->i_sem); newattrs.ia_size = length; newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; @@ -295,11 +298,16 @@ asmlinkage int sys_access(const char * filename, int mode) /* Clear the capabilities if we switch to a non-root user */ if (current->uid) cap_clear(current->cap_effective); - + else + current->cap_effective = current->cap_permitted; + dentry = namei(filename); res = PTR_ERR(dentry); if (!IS_ERR(dentry)) { res = permission(dentry->d_inode, mode); + /* SuS v2 requires we report a read only fs too */ + if(!res && (mode & S_IWOTH) && IS_RDONLY(dentry->d_inode)) + res = -EROFS; dput(dentry); } @@ -752,12 +760,7 @@ out_error: */ asmlinkage int sys_creat(const char * pathname, int mode) { - int ret; - - lock_kernel(); - ret = sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); - unlock_kernel(); - return ret; + return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); } #endif @@ -782,7 +785,7 @@ void __fput(struct file *filp) * "id" is the POSIX thread ID. We use the * files pointer for this.. */ -int close_fp(struct file *filp, fl_owner_t id) +int filp_close(struct file *filp, fl_owner_t id) { int retval; struct dentry *dentry = filp->f_dentry; @@ -818,7 +821,7 @@ asmlinkage int sys_close(unsigned int fd) files->fd[fd] = NULL; put_unused_fd(fd); FD_CLR(fd, &files->close_on_exec); - error = close_fp(filp, files); + error = filp_close(filp, files); } unlock_kernel(); return error; diff --git a/fs/proc/array.c b/fs/proc/array.c index d845ca5d3..1ce95cdb4 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -565,7 +565,23 @@ static unsigned long get_wchan(struct task_struct *p) } while (count++ < 16); } #elif defined(__powerpc__) - return (p->tss.wchan); + { + unsigned long ip, sp; + unsigned long stack_page = (unsigned long) p; + int count = 0; + + sp = p->tss.ksp; + do { + sp = *(unsigned long *)sp; + if (sp < stack_page || sp >= stack_page + 8188) + return 0; + if (count > 0) { + ip = *(unsigned long *)(sp + 4); + if (ip < first_sched || ip >= last_sched) + return ip; + } + } while (count++ < 16); + } #elif defined (CONFIG_ARM) { unsigned long fp, lr; @@ -902,7 +918,7 @@ static int get_stat(int pid, char * buffer) return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu \ -%lu %lu %lu %lu %lu %lu %lu %lu %d\n", +%lu %lu %lu %lu %lu %lu %lu %lu %d %d\n", pid, tsk->comm, state, @@ -944,7 +960,8 @@ static int get_stat(int pid, char * buffer) wchan, tsk->nswap, tsk->cnswap, - tsk->exit_signal); + tsk->exit_signal, + tsk->processor); } static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size, @@ -1369,6 +1386,7 @@ static int process_unauthorized(int type, int pid) { struct task_struct *p; uid_t euid=0; /* Save the euid keep the lock short */ + int ok = 0; read_lock(&tasklist_lock); @@ -1378,9 +1396,11 @@ static int process_unauthorized(int type, int pid) */ p = find_task_by_pid(pid); - if(p) - { + if (p) { euid=p->euid; + ok = p->dumpable; + if(!cap_issubset(p->cap_permitted, current->cap_permitted)) + ok=0; if(!p->mm) /* Scooby scooby doo where are you ? */ p=NULL; } @@ -1400,7 +1420,7 @@ static int process_unauthorized(int type, int pid) case PROC_PID_CPU: return 0; } - if(capable(CAP_DAC_OVERRIDE) || current->fsuid == euid) + if(capable(CAP_DAC_OVERRIDE) || (current->fsuid == euid && ok)) return 0; return 1; } diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 8db99f7f4..1defdbae1 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -19,7 +19,7 @@ #include <asm/uaccess.h> static int proc_readfd(struct file *, void *, filldir_t); -static int proc_lookupfd(struct inode *, struct dentry *); +static struct dentry *proc_lookupfd(struct inode *, struct dentry *); static struct file_operations proc_fd_operations = { NULL, /* lseek - default */ @@ -67,7 +67,7 @@ struct inode_operations proc_fd_inode_operations = { * * Thus just return -ENOENT instead. */ -static int proc_lookupfd(struct inode * dir, struct dentry * dentry) +static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry) { unsigned int ino, pid, fd, c; struct task_struct * p; @@ -77,13 +77,11 @@ static int proc_lookupfd(struct inode * dir, struct dentry * dentry) int len, err; err = -ENOENT; - if (!dir) - goto out; ino = dir->i_ino; pid = ino >> 16; ino &= 0x0000ffff; - if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) + if (!pid || ino != PROC_PID_FD) goto out; fd = 0; @@ -121,10 +119,10 @@ static int proc_lookupfd(struct inode * dir, struct dentry * dentry) if (inode) { dentry->d_op = &proc_dentry_operations; d_add(dentry, inode); - err = 0; + return NULL; } out: - return err; + return ERR_PTR(err); } #define NUMBUF 10 @@ -137,10 +135,6 @@ static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) int retval; char buf[NUMBUF]; - retval = -EBADF; - if (!inode || !S_ISDIR(inode->i_mode)) - goto out; - retval = 0; ino = inode->i_ino; pid = ino >> 16; diff --git a/fs/proc/link.c b/fs/proc/link.c index 70c31f21a..b12050767 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -158,7 +158,7 @@ static int do_proc_readlink(struct dentry *dentry, char * buffer, int buflen) path = tmp; } else { path = d_path(dentry, tmp, PAGE_SIZE); - len = tmp + PAGE_SIZE - path; + len = tmp + PAGE_SIZE - 1 - path; } if (len < buflen) diff --git a/fs/proc/openpromfs.c b/fs/proc/openpromfs.c index 2c2c84784..dcb007f11 100644 --- a/fs/proc/openpromfs.c +++ b/fs/proc/openpromfs.c @@ -1,4 +1,4 @@ -/* $Id: openpromfs.c,v 1.32 1998/11/18 06:15:20 davem Exp $ +/* $Id: openpromfs.c,v 1.33 1999/04/28 11:57:33 davem Exp $ * openpromfs.c: /proc/openprom handling routines * * Copyright (C) 1996-1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -59,7 +59,7 @@ static struct openpromfs_dev **devices; static int openpromfs_create (struct inode *, struct dentry *, int); static int openpromfs_readdir(struct file *, void *, filldir_t); -static int openpromfs_lookup(struct inode *, struct dentry *dentry); +static struct dentry *openpromfs_lookup(struct inode *, struct dentry *dentry); static int openpromfs_unlink (struct inode *, struct dentry *dentry); static ssize_t nodenum_read(struct file *file, char *buf, @@ -685,7 +685,7 @@ static int lookup_children(u16 n, const char * name, int len) return 0; } -static int openpromfs_lookup(struct inode * dir, struct dentry *dentry) +static struct dentry *openpromfs_lookup(struct inode * dir, struct dentry *dentry) { int ino = 0; #define OPFSL_DIR 0 @@ -776,11 +776,11 @@ static int openpromfs_lookup(struct inode * dir, struct dentry *dentry) if (ino) type = OPFSL_DIR; else - return -ENOENT; + return ERR_PTR(-ENOENT); } inode = proc_get_inode (dir->i_sb, ino, 0); if (!inode) - return -EINVAL; + return ERR_PTR(-EINVAL); switch (type) { case OPFSL_DIR: inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; @@ -827,7 +827,7 @@ static int openpromfs_lookup(struct inode * dir, struct dentry *dentry) inode->i_uid = 0; d_add(dentry, inode); - return 0; + return NULL; } static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filldir) @@ -842,7 +842,6 @@ static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filld struct openpromfs_dev *d; char buffer2[64]; - if (!inode || !S_ISDIR (inode->i_mode)) return -ENOTDIR; ino = inode->i_ino; i = filp->f_pos; switch (i) { diff --git a/fs/proc/root.c b/fs/proc/root.c index 9c841a88e..82b3fd71d 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -28,7 +28,7 @@ #define FIRST_PROCESS_ENTRY 256 static int proc_root_readdir(struct file *, void *, filldir_t); -static int proc_root_lookup(struct inode *,struct dentry *); +static struct dentry *proc_root_lookup(struct inode *,struct dentry *); static int proc_unlink(struct inode *, struct dentry *); static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0}; @@ -181,14 +181,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 file *, void *, filldir_t); -static int (*proc_openprom_deflookup_ptr)(struct inode *, struct dentry *); +static struct dentry * (*proc_openprom_deflookup_ptr)(struct inode *, struct dentry *); 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 file *, void *, filldir_t), - int (*lookup)(struct inode *, struct dentry *), + struct dentry * (*lookup)(struct inode *, struct dentry *), void (*use)(struct inode *, int), struct openpromfs_dev ***devices) { @@ -250,7 +250,7 @@ proc_openprom_defreaddir(struct file * filp, void * dirent, filldir_t filldir) } #define OPENPROM_DEFREADDIR proc_openprom_defreaddir -static int +static struct dentry * proc_openprom_deflookup(struct inode * dir, struct dentry *dentry) { request_module("openpromfs"); @@ -258,7 +258,7 @@ proc_openprom_deflookup(struct inode * dir, struct dentry *dentry) proc_openprom_deflookup) return proc_openprom_inode_operations.lookup (dir, dentry); - return -ENOENT; + return ERR_PTR(-ENOENT); } #define OPENPROM_DEFLOOKUP proc_openprom_deflookup #else @@ -779,16 +779,12 @@ struct dentry_operations proc_dentry_operations = * Don't create negative dentries here, return -ENOENT by hand * instead. */ -int proc_lookup(struct inode * dir, struct dentry *dentry) +struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry) { struct inode *inode; struct proc_dir_entry * de; int error; - error = -ENOTDIR; - if (!dir || !S_ISDIR(dir->i_mode)) - goto out; - error = -ENOENT; inode = NULL; de = (struct proc_dir_entry *) dir->u.generic_ip; @@ -810,13 +806,12 @@ int proc_lookup(struct inode * dir, struct dentry *dentry) if (inode) { dentry->d_op = &proc_dentry_operations; d_add(dentry, inode); - error = 0; + return NULL; } -out: - return error; + return ERR_PTR(error); } -static int proc_root_lookup(struct inode * dir, struct dentry * dentry) +static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry) { unsigned int pid, c; struct task_struct *p; @@ -836,7 +831,7 @@ static int proc_root_lookup(struct inode * dir, struct dentry * dentry) } if (!proc_lookup(dir, dentry)) - return 0; + return NULL; pid = 0; name = dentry->d_name.name; @@ -863,13 +858,13 @@ static int proc_root_lookup(struct inode * dir, struct dentry * dentry) unsigned long ino = (pid << 16) + PROC_PID_INO; inode = proc_get_inode(dir->i_sb, ino, &proc_pid); if (!inode) - return -EINVAL; + return ERR_PTR(-EINVAL); inode->i_flags|=S_IMMUTABLE; } dentry->d_op = &proc_dentry_operations; d_add(dentry, inode); - return 0; + return NULL; } /* @@ -889,8 +884,6 @@ int proc_readdir(struct file * filp, int i; struct inode *inode = filp->f_dentry->d_inode; - if (!inode || !S_ISDIR(inode->i_mode)) - return -ENOTDIR; ino = inode->i_ino; de = (struct proc_dir_entry *) inode->u.generic_ip; if (!de) diff --git a/fs/qnx4/dir.c b/fs/qnx4/dir.c index 119930667..029fd9061 100644 --- a/fs/qnx4/dir.c +++ b/fs/qnx4/dir.c @@ -35,9 +35,6 @@ static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir) blknum = inode->u.qnx4_i.i_first_xtnt.xtnt_blk - 1 + ((filp->f_pos >> 6) >> 3); - if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) { - return -EBADF; - } QNX4DEBUG(("qnx4_readdir:i_size = %ld\n", (long) inode->i_size)); QNX4DEBUG(("filp->f_pos = %ld\n", (long) filp->f_pos)); QNX4DEBUG(("BlkNum = %ld\n", (long) blknum)); diff --git a/fs/qnx4/file.c b/fs/qnx4/file.c index 499980126..ab923b7f0 100644 --- a/fs/qnx4/file.c +++ b/fs/qnx4/file.c @@ -33,8 +33,6 @@ #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) -#include <linux/fs.h> -#include <linux/qnx4_fs.h> static int qnx4_readpage(struct file *file, struct page *page); diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c index 267ecb110..feb014b8e 100644 --- a/fs/qnx4/namei.c +++ b/fs/qnx4/namei.c @@ -70,12 +70,8 @@ static struct buffer_head *qnx4_find_entry(int len, struct inode *dir, struct buffer_head *bh; *res_dir = NULL; - if (!dir || !dir->i_sb) { - if (!dir) { - printk("qnx4: NULL dir.\n"); - } else { - printk("qnx4: no superblock on dir.\n"); - } + if (!dir->i_sb) { + printk("qnx4: no superblock on dir.\n"); return NULL; } bh = NULL; @@ -108,7 +104,7 @@ static struct buffer_head *qnx4_find_entry(int len, struct inode *dir, return NULL; } -int qnx4_lookup(struct inode *dir, struct dentry *dentry) +struct dentry * qnx4_lookup(struct inode *dir, struct dentry *dentry) { int ino; struct qnx4_inode_entry *de; @@ -116,17 +112,10 @@ int qnx4_lookup(struct inode *dir, struct dentry *dentry) struct buffer_head *bh; const char *name = dentry->d_name.name; int len = dentry->d_name.len; - struct inode *foundinode; + struct inode *foundinode = NULL; - if (!dir) { - return -EBADF; - } - if (!S_ISDIR(dir->i_mode)) { - return -EBADF; - } - if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino))) { - return -ENOENT; - } + if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino))) + goto out; /* The entry is linked, let's get the real info */ if ((de->di_status & QNX4_FILE_LINK) == QNX4_FILE_LINK) { lnk = (struct qnx4_link_info *) de; @@ -137,11 +126,12 @@ int qnx4_lookup(struct inode *dir, struct dentry *dentry) if ((foundinode = iget(dir->i_sb, ino)) == NULL) { QNX4DEBUG(("qnx4: lookup->iget -> NULL\n")); - return -EACCES; + return ERR_PTR(-EACCES); } +out: d_add(dentry, foundinode); - return 0; + return NULL; } #ifdef CONFIG_QNX4FS_RW diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index 211563b1c..fd374842e 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -267,9 +267,6 @@ romfs_readdir(struct file *filp, void *dirent, filldir_t filldir) int stored = 0; char fsname[ROMFS_MAXFN]; /* XXX dynamic? */ - if (!i || !S_ISDIR(i->i_mode)) - return -EBADF; - maxoff = i->i_sb->u.romfs_sb.s_maxsize; offset = filp->f_pos; @@ -312,7 +309,7 @@ romfs_readdir(struct file *filp, void *dirent, filldir_t filldir) } } -static int +static struct dentry * romfs_lookup(struct inode *dir, struct dentry *dentry) { unsigned long offset, maxoff; @@ -323,10 +320,6 @@ romfs_lookup(struct inode *dir, struct dentry *dentry) const char *name; /* got from dentry */ int len; - res = -EBADF; - if (!dir || !S_ISDIR(dir->i_mode)) - goto out; - res = 0; /* instead of ENOENT */ offset = dir->i_ino & ROMFH_MASK; if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) @@ -379,7 +372,7 @@ romfs_lookup(struct inode *dir, struct dentry *dentry) } out: - return res; + return ERR_PTR(res); } /* diff --git a/fs/smbfs/cache.c b/fs/smbfs/cache.c index 292493dac..36f1650ac 100644 --- a/fs/smbfs/cache.c +++ b/fs/smbfs/cache.c @@ -12,7 +12,6 @@ #include <linux/errno.h> #include <linux/kernel.h> #include <linux/mm.h> -#include <linux/errno.h> #include <linux/dirent.h> #include <linux/smb_fs.h> @@ -142,7 +141,7 @@ smb_add_to_cache(struct cache_head * cachep, struct cache_dirent *entry, #ifdef SMBFS_DEBUG_VERBOSE printk("smb_add_to_cache: cache inode %p, status %d, adding %s at %ld\n", -inode, cachep->status, entry->d_name, fpos); +inode, cachep->status, entry->name, fpos); #endif /* * Don't do anything if we've had an error ... @@ -171,7 +170,7 @@ inode, cachep->status, entry->d_name, fpos); cachep->entries++; #ifdef SMBFS_DEBUG_VERBOSE printk("smb_add_to_cache: added entry %s, len=%d, pos=%ld, entries=%d\n", -entry->d_name, len, fpos, cachep->entries); +entry->name, len, fpos, cachep->entries); #endif return; } diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index 30138872b..4f942db80 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -22,7 +22,7 @@ static ssize_t smb_dir_read(struct file *, char *, size_t, loff_t *); static int smb_readdir(struct file *, void *, filldir_t); static int smb_dir_open(struct inode *, struct file *); -static int smb_lookup(struct inode *, struct dentry *); +static struct dentry *smb_lookup(struct inode *, struct dentry *); static int smb_create(struct inode *, struct dentry *, int); static int smb_mkdir(struct inode *, struct dentry *, int); static int smb_rmdir(struct inode *, struct dentry *); @@ -191,14 +191,14 @@ file->f_dentry->d_name.name); /* * Dentry operations routines */ -static int smb_lookup_validate(struct dentry *); +static int smb_lookup_validate(struct dentry *, int); static int smb_hash_dentry(struct dentry *, struct qstr *); static int smb_compare_dentry(struct dentry *, struct qstr *, struct qstr *); static void smb_delete_dentry(struct dentry *); static struct dentry_operations smbfs_dentry_operations = { - smb_lookup_validate, /* d_validate(struct dentry *) */ + smb_lookup_validate, /* d_revalidate(struct dentry *) */ smb_hash_dentry, /* d_hash */ smb_compare_dentry, /* d_compare */ smb_delete_dentry /* d_delete(struct dentry *) */ @@ -208,7 +208,7 @@ static struct dentry_operations smbfs_dentry_operations = * This is the callback when the dcache has a lookup hit. */ static int -smb_lookup_validate(struct dentry * dentry) +smb_lookup_validate(struct dentry * dentry, int flags) { struct inode * inode = dentry->d_inode; unsigned long age = jiffies - dentry->d_time; @@ -324,7 +324,7 @@ smb_renew_times(struct dentry * dentry) } } -static int +static struct dentry * smb_lookup(struct inode *dir, struct dentry *dentry) { struct smb_fattr finfo; @@ -360,7 +360,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, error); } } out: - return error; + return ERR_PTR(error); } /* @@ -423,9 +423,6 @@ smb_create(struct inode *dir, struct dentry *dentry, int mode) printk("smb_create: creating %s/%s, mode=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, mode); #endif - error = -ENAMETOOLONG; - if (dentry->d_name.len > SMB_MAXNAMELEN) - goto out; smb_invalid_dir_cache(dir); error = smb_proc_create(dentry, 0, CURRENT_TIME, &fileid); @@ -439,7 +436,6 @@ printk("smb_create: %s/%s failed, error=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, error); #endif } -out: return error; } @@ -449,17 +445,12 @@ smb_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int error; - error = -ENAMETOOLONG; - if (dentry->d_name.len > SMB_MAXNAMELEN) - goto out; - smb_invalid_dir_cache(dir); error = smb_proc_mkdir(dentry); if (!error) { error = smb_instantiate(dentry, 0, 0); } -out: return error; } @@ -514,11 +505,6 @@ smb_rename(struct inode *old_dir, struct dentry *old_dentry, { int error; - error = -ENAMETOOLONG; - if (old_dentry->d_name.len > SMB_MAXNAMELEN || - new_dentry->d_name.len > SMB_MAXNAMELEN) - goto out; - /* * Close any open files, and check whether to delete the * target before attempting the rename. @@ -537,6 +523,7 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name, error); #endif goto out; } + /* FIXME */ d_delete(new_dentry); } @@ -547,7 +534,6 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name, error); { smb_renew_times(old_dentry); smb_renew_times(new_dentry); - d_move(old_dentry, new_dentry); } out: return error; diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index a6cf24ce1..aff45ef9b 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -144,7 +144,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, count, offset, wsize); result = smb_proc_write(dentry, offset, wsize, buffer); if (result < 0) - goto io_error; + break; /* N.B. what if result < wsize?? */ #ifdef SMBFS_PARANOIA if (result < wsize) @@ -162,15 +162,7 @@ printk("smb_writepage_sync: short write, wsize=%d, result=%d\n", wsize, result); inode->i_size = offset; inode->u.smbfs_i.cache_valid |= SMB_F_LOCALWRITE; } while (count); - -out: - smb_unlock_page(page); return written ? written : result; - -io_error: - /* Must mark the page invalid after I/O error */ - clear_bit(PG_uptodate, &page->flags); - goto out; } /* @@ -190,6 +182,7 @@ smb_writepage(struct file *file, struct page *page) set_bit(PG_locked, &page->flags); atomic_inc(&page->count); result = smb_writepage_sync(dentry, page, 0, PAGE_SIZE); + smb_unlock_page(page); free_page(page_address(page)); return result; } diff --git a/fs/super.c b/fs/super.c index 7c5d2904c..690807a26 100644 --- a/fs/super.c +++ b/fs/super.c @@ -559,6 +559,7 @@ static struct super_block * read_super(kdev_t dev,const char *name,int flags, s->s_dev = dev; s->s_flags = flags; s->s_dirt = 0; + sema_init(&s->s_vfs_rename_sem,1); /* N.B. Should lock superblock now ... */ if (!type->read_super(s, data, silent)) goto out_fail; @@ -1130,6 +1131,7 @@ void __init mount_root(void) sb = get_empty_super(); /* "can't fail" */ sb->s_dev = get_unnamed_dev(); sb->s_flags = root_mountflags; + sema_init(&sb->s_vfs_rename_sem,1); vfsmnt = add_vfsmnt(sb, "/dev/root", "/"); if (vfsmnt) { if (nfs_root_mount(sb) >= 0) { @@ -1155,12 +1157,22 @@ void __init mount_root(void) #ifdef CONFIG_BLK_DEV_FD if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) { +#ifdef CONFIG_BLK_DEV_RAM + extern int rd_doload; +#endif floppy_eject(); #ifndef CONFIG_BLK_DEV_RAM printk(KERN_NOTICE "(Warning, this kernel has no ramdisk support)\n"); +#else + /* rd_doload is 2 for a dual initrd/ramload setup */ + if(rd_doload==2) + rd_load_secondary(); + else #endif - printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER\n"); - wait_for_keypress(); + { + printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER\n"); + wait_for_keypress(); + } } #endif diff --git a/fs/sysv/CHANGES b/fs/sysv/CHANGES index 4dbff5b56..94507925c 100644 --- a/fs/sysv/CHANGES +++ b/fs/sysv/CHANGES @@ -1,45 +1,55 @@ -Mon Dec 15 1997 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl> +Mon, 15 Dec 1997 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl> * namei.c: struct sysv_dir_inode_operations updated to use dentries. -Fri Jan 23 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl> +Fri, 23 Jan 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl> * inode.c: corrected 1 track offset setting (in sb->sv_block_base). Originally it was overridden (by setting to zero) in detected_[xenix,sysv4,sysv2,coherent]. Thanks to Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl> for identifying the problem. -Tue Jan 27 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl> +Tue, 27 Jan 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl> * inode.c: added 2048-byte block support to SystemV FS. Merged detected_bs[512,1024,2048]() into one function: void detected_bs (u_char type, struct super_block *sb). Thanks to Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl> for the patch. -Wed Feb 4 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl> +Wed, 4 Feb 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl> * namei.c: removed static subdir(); is_subdir() from dcache.c is used instead. Cosmetic changes. -Thu Dec 3 1998 Al Viro (viro@math.psu.edu) +Thu, 3 Dec 1998 Al Viro (viro@math.psu.edu) * namei.c (sysv_rmdir): Bugectomy: old check for victim being busy (inode->i_count) wasn't replaced (with checking dentry->d_count) and escaped Linus in the last round of changes. Shot and buried. -Wed Dec 9 1998 AV +Wed, 9 Dec 1998 AV * namei.c (do_sysv_rename): Fixed incorrect check for other owners + race. Removed checks that went to VFS. * namei.c (sysv_unlink): Removed checks that went to VFS. -Thu Dec 10 1998 AV +Thu, 10 Dec 1998 AV * namei.c (do_mknod): Removed dead code - mknod is never asked to create a symlink or directory. Incidentially, it wouldn't do it right if it would be called. -Sat Dec 26 1998 KGB +Sat, 26 Dec 1998 KGB * inode.c (detect_sysv4): Added detection of expanded s_type field (0x10, 0x20 and 0x30). Forced read-only access in this case. + +Sun, 21 Mar 1999 AV + * namei.c (sysv_link): + Fixed i_count usage that resulted in dcache corruption. + * inode.c: + Filled ->delete_inode() method with sysv_delete_inode(). + sysv_put_inode() is gone, as it tried to do ->delete_ + _inode()'s job. + * ialloc.c: (sysv_free_inode): + Fixed race. diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index 9a1acc19e..a28a7ba8e 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -69,14 +69,12 @@ struct inode_operations sysv_dir_inode_operations = { static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; - struct super_block * sb; + struct super_block * sb = inode->i_sb; unsigned int offset,i; struct buffer_head * bh; char* bh_data; struct sysv_dir_entry * de, sde; - if (!inode || !(sb = inode->i_sb) || !S_ISDIR(inode->i_mode)) - return -EBADF; if ((unsigned long)(filp->f_pos) % SYSV_DIRSIZE) return -EBADF; while (filp->f_pos < inode->i_size) { diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c index 8718b8b5a..2826286cc 100644 --- a/fs/sysv/ialloc.c +++ b/fs/sysv/ialloc.c @@ -86,6 +86,7 @@ void sysv_free_inode(struct inode * inode) return; } raw_inode = (struct sysv_inode *) bh->b_data + ((ino-1) & sb->sv_inodes_per_block_1); + clear_inode(inode); lock_super(sb); if (*sb->sv_sb_fic_count < sb->sv_fic_size) *sv_sb_fic_inode(sb,(*sb->sv_sb_fic_count)++) = ino; @@ -97,7 +98,6 @@ void sysv_free_inode(struct inode * inode) mark_buffer_dirty(bh, 1); unlock_super(sb); brelse(bh); - clear_inode(inode); } struct inode * sysv_new_inode(const struct inode * dir) diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index d5af68176..f58560996 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -55,10 +55,8 @@ void sysv_print_inode(struct inode * inode) } #endif -void sysv_put_inode(struct inode *inode) +static void sysv_delete_inode(struct inode *inode) { - if (inode->i_nlink) - return; inode->i_size = 0; sysv_truncate(inode); sysv_free_inode(inode); @@ -68,8 +66,8 @@ void sysv_put_inode(struct inode *inode) static struct super_operations sysv_sops = { sysv_read_inode, sysv_write_inode, - sysv_put_inode, - NULL, /* delete_inode */ + NULL, /* nothing special on put_inode() */ + sysv_delete_inode, sysv_notify_change, sysv_put_super, sysv_write_super, diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index 1ae336647..8cea266a8 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -67,8 +67,6 @@ static struct buffer_head * sysv_find_entry(struct inode * dir, struct buffer_head * bh; *res_dir = NULL; - if (!dir) - return NULL; sb = dir->i_sb; if (namelen > SYSV_NAMELEN) { if (sb->sv_truncate) @@ -103,17 +101,12 @@ static struct buffer_head * sysv_find_entry(struct inode * dir, return NULL; } -int sysv_lookup(struct inode * dir, struct dentry * dentry) +struct dentry *sysv_lookup(struct inode * dir, struct dentry * dentry) { struct inode * inode = NULL; struct sysv_dir_entry * de; struct buffer_head * bh; - if (!dir) - return -ENOENT; - if (!S_ISDIR(dir->i_mode)) { - return -ENOENT; - } bh = sysv_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); if (bh) { @@ -122,10 +115,10 @@ int sysv_lookup(struct inode * dir, struct dentry * dentry) inode = iget(dir->i_sb, ino); if (!inode) - return -EACCES; + return ERR_PTR(-EACCES); } d_add(dentry, inode); - return 0; + return NULL; } /* @@ -209,8 +202,6 @@ int sysv_create(struct inode * dir, struct dentry * dentry, int mode) struct buffer_head * bh; struct sysv_dir_entry * de; - if (!dir) - return -ENOENT; inode = sysv_new_inode(dir); if (!inode) return -ENOSPC; @@ -239,8 +230,6 @@ int sysv_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev) struct buffer_head * bh; struct sysv_dir_entry * de; - if (!dir) - return -ENOENT; bh = sysv_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); if (bh) { @@ -286,8 +275,6 @@ int sysv_mkdir(struct inode * dir, struct dentry *dentry, int mode) struct buffer_head * bh, *dir_block; struct sysv_dir_entry * de; - if (!dir) - return -EINVAL; bh = sysv_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); if (bh) { @@ -576,6 +563,7 @@ int sysv_link(struct dentry * old_dentry, struct inode * dir, oldinode->i_nlink++; oldinode->i_ctime = CURRENT_TIME; mark_inode_dirty(oldinode); + oldinode->i_count++; d_instantiate(dentry, oldinode); return 0; } @@ -593,8 +581,8 @@ int sysv_link(struct dentry * old_dentry, struct inode * dir, * 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, struct dentry * old_dentry, - struct inode * new_dir, struct dentry * new_dentry) +int 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; @@ -627,20 +615,8 @@ start_up: new_bh = NULL; } } - if (new_inode == old_inode) { - retval = 0; - goto end_rename; - } if (S_ISDIR(old_inode->i_mode)) { - retval = -EINVAL; - if (is_subdir(new_dentry, old_dentry)) - goto end_rename; if (new_inode) { - if (new_dentry->d_count > 1) - shrink_dcache_parent(new_dentry); - retval = -EBUSY; - if (new_dentry->d_count > 1) - goto end_rename; retval = -ENOTEMPTY; if (!empty_dir(new_inode)) goto end_rename; @@ -695,7 +671,6 @@ start_up: mark_inode_dirty(new_dir); } } - d_move(old_dentry, new_dentry); retval = 0; end_rename: brelse(dir_bh); @@ -703,29 +678,3 @@ end_rename: brelse(new_bh); return retval; } - -/* - * Ok, rename also locks out other renames, as they can change the parent of - * a directory, and we don't want any races. Other races are checked for by - * "do_rename()", which restarts if there are inconsistencies. - * - * Note that there is no race between different filesystems: it's only within - * 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, struct dentry * old_dentry, - struct inode * new_dir, struct dentry * new_dentry) -{ - static struct wait_queue * wait = NULL; - static int lock = 0; - int result; - - while (lock) - sleep_on(&wait); - lock = 1; - result = do_sysv_rename(old_dir, old_dentry, - new_dir, new_dentry); - lock = 0; - wake_up(&wait); - return result; -} diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index 24810fe48..6a79c4a5e 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -43,13 +43,6 @@ ufs_readdir (struct file * filp, void * dirent, filldir_t filldir) int de_reclen; unsigned flags, swab; - - /* Isn't that already done in the upper layer??? - * the VFS layer really needs some explicit documentation! - */ - if (!inode || !S_ISDIR(inode->i_mode)) - return -EBADF; - sb = inode->i_sb; swab = sb->u.ufs_sb.s_swab; flags = sb->u.ufs_sb.s_flags; diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index dc5dccf4c..3daf77c57 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -91,8 +91,6 @@ static struct buffer_head * ufs_find_entry (struct inode * dir, UFSD(("ENTER, dir_ino %lu, name %s, namlen %u\n", dir->i_ino, name, namelen)) *res_dir = NULL; - if (!dir) - return NULL; sb = dir->i_sb; flags = sb->u.ufs_sb.s_flags; @@ -185,7 +183,7 @@ failed: return NULL; } -int ufs_lookup(struct inode * dir, struct dentry *dentry) +struct dentry *ufs_lookup(struct inode * dir, struct dentry *dentry) { struct super_block * sb; struct inode * inode; @@ -199,7 +197,7 @@ int ufs_lookup(struct inode * dir, struct dentry *dentry) swab = sb->u.ufs_sb.s_swab; if (dentry->d_name.len > UFS_MAXNAMLEN) - return -ENAMETOOLONG; + return ERR_PTR(-ENAMETOOLONG); bh = ufs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); inode = NULL; @@ -208,11 +206,11 @@ int ufs_lookup(struct inode * dir, struct dentry *dentry) brelse (bh); inode = iget(sb, ino); if (!inode) - return -EACCES; + return ERR_PTR(-EACCES); } d_add(dentry, inode); UFSD(("EXIT\n")) - return 0; + return NULL; } /* @@ -250,12 +248,6 @@ static struct buffer_head * ufs_add_entry (struct inode * dir, swab = sb->u.ufs_sb.s_swab; uspi = sb->u.ufs_sb.s_uspi; - if (namelen > UFS_MAXNAMLEN) - { - *err = -ENAMETOOLONG; - return NULL; - } - if (!namelen) return NULL; /* @@ -469,10 +461,6 @@ int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) sb = dir->i_sb; flags = sb->u.ufs_sb.s_flags; swab = sb->u.ufs_sb.s_swab; - - err = -ENAMETOOLONG; - if (dentry->d_name.len > UFS_MAXNAMLEN) - goto out; inode = ufs_new_inode (dir, mode, &err); if (!inode) @@ -528,10 +516,6 @@ int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode) sb = dir->i_sb; flags = sb->u.ufs_sb.s_flags; swab = sb->u.ufs_sb.s_swab; - - err = -ENAMETOOLONG; - if (dentry->d_name.len > UFS_MAXNAMLEN) - goto out; err = -EMLINK; if (dir->i_nlink >= UFS_LINK_MAX) @@ -670,10 +654,6 @@ int ufs_rmdir (struct inode * dir, struct dentry *dentry) swab = sb->u.ufs_sb.s_swab; UFSD(("ENTER\n")) - - retval = -ENAMETOOLONG; - if (dentry->d_name.len > UFS_MAXNAMLEN) - goto out; retval = -ENOENT; bh = ufs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); @@ -687,14 +667,12 @@ int ufs_rmdir (struct inode * dir, struct dentry *dentry) if (SWAB32(de->d_ino) != inode->i_ino) goto end_rmdir; + retval = -ENOTEMPTY; if (!ufs_empty_dir (inode)) - retval = -ENOTEMPTY; - else if (SWAB32(de->d_ino) != inode->i_ino) - retval = -ENOENT; - else { - retval = ufs_delete_entry (dir, de, bh); - dir->i_version = ++event; - } + goto end_rmdir; + + retval = ufs_delete_entry (dir, de, bh); + dir->i_version = ++event; if (retval) goto end_rmdir; mark_buffer_dirty(bh, 1); @@ -717,7 +695,6 @@ int ufs_rmdir (struct inode * dir, struct dentry *dentry) end_rmdir: brelse (bh); -out: UFSD(("EXIT\n")) return retval; @@ -735,10 +712,6 @@ int ufs_unlink(struct inode * dir, struct dentry *dentry) sb = dir->i_sb; flags = sb->u.ufs_sb.s_flags; swab = sb->u.ufs_sb.s_swab; - - retval = -ENAMETOOLONG; - if (dentry->d_name.len > UFS_MAXNAMLEN) - goto out; retval = -ENOENT; bh = ufs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); @@ -779,7 +752,6 @@ int ufs_unlink(struct inode * dir, struct dentry *dentry) end_unlink: brelse (bh); -out: return retval; } @@ -881,9 +853,6 @@ int ufs_link (struct dentry * old_dentry, struct inode * dir, if (S_ISDIR(inode->i_mode)) return -EPERM; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - return -EPERM; - if (inode->i_nlink >= UFS_LINK_MAX) return -EMLINK; @@ -912,17 +881,10 @@ int ufs_link (struct dentry * old_dentry, struct inode * dir, ((struct ufs_dir_entry *) ((char *) buffer + \ SWAB16(((struct ufs_dir_entry *) buffer)->d_reclen)))->d_ino /* - * rename uses retrying to avoid race-conditions: at least they should be - * minimal. - * it tries to allocate all the blocks, then sanity-checks, and if the sanity- - * checks fail, it tries to restart itself again. Very practical - no changes - * are done until we know everything works ok.. and then all the changes can be - * done in one fell swoop when we have claimed all the buffers needed. - * * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ -static int do_ufs_rename (struct inode * old_dir, struct dentry * old_dentry, +int ufs_rename (struct inode * old_dir, struct dentry * old_dentry, struct inode * new_dir, struct dentry * new_dentry ) { struct super_block * sb; @@ -941,9 +903,6 @@ static int do_ufs_rename (struct inode * old_dir, struct dentry * old_dentry, old_inode = new_inode = NULL; old_bh = new_bh = dir_bh = NULL; new_de = NULL; - retval = -ENAMETOOLONG; - if (old_dentry->d_name.len > UFS_MAXNAMLEN) - goto end_rename; old_bh = ufs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); /* @@ -967,25 +926,14 @@ static int do_ufs_rename (struct inode * old_dir, struct dentry * old_dentry, DQUOT_INIT(new_inode); } } - retval = 0; - if (new_inode == old_inode) - goto end_rename; if (S_ISDIR(old_inode->i_mode)) { - retval = -EINVAL; - if (is_subdir(new_dentry, old_dentry)) - goto end_rename; if (new_inode) { - /* Prune any children before testing for busy */ - if (new_dentry->d_count > 1) - shrink_dcache_parent(new_dentry); - retval = -EBUSY; - if (new_dentry->d_count > 1) - goto end_rename; retval = -ENOTEMPTY; if (!ufs_empty_dir (new_inode)) goto end_rename; } + retval = -EIO; dir_bh = ufs_bread (old_inode, 0, 0, &retval); if (!dir_bh) goto end_rename; @@ -1042,8 +990,6 @@ static int do_ufs_rename (struct inode * old_dir, struct dentry * old_dentry, wait_on_buffer (new_bh); } - /* Update the dcache */ - d_move(old_dentry, new_dentry); retval = 0; end_rename: brelse (dir_bh); @@ -1054,36 +1000,3 @@ end_rename: return retval; } - -/* - * Ok, rename also locks out other renames, as they can change the parent of - * a directory, and we don't want any races. Other races are checked for by - * "do_rename()", which restarts if there are inconsistencies. - * - * Note that there is no race between different filesystems: it's only within - * the same device that races occur: many renames can happen at once, as long - * as they are on different partitions. - * - * In the second extended file system, we use a lock flag stored in the memory - * super-block. This way, we really lock other renames only if they occur - * on the same file system - */ -int ufs_rename (struct inode * old_dir, struct dentry *old_dentry, - struct inode * new_dir, struct dentry *new_dentry ) -{ - int result; - - UFSD(("ENTER\n")) - - while (old_dir->i_sb->u.ufs_sb.s_rename_lock) - sleep_on (&old_dir->i_sb->u.ufs_sb.s_rename_wait); - old_dir->i_sb->u.ufs_sb.s_rename_lock = 1; - result = do_ufs_rename (old_dir, old_dentry, new_dir, new_dentry); - old_dir->i_sb->u.ufs_sb.s_rename_lock = 0; - wake_up (&old_dir->i_sb->u.ufs_sb.s_rename_wait); - - UFSD(("EXIT\n")) - - return result; -} - diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 1abdbe038..4fe11c564 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -125,7 +125,7 @@ void ufs_print_super_stuff(struct ufs_super_block_first * usb1, printk(" cssize: %u\n", SWAB32(usb1->fs_cssize)); printk(" cgsize: %u\n", SWAB32(usb1->fs_cgsize)); printk(" fstodb: %u\n", SWAB32(usb1->fs_fsbtodb)); - printk(" contigsumsize: %d\n", SWAB32(usb3->fs_u.fs_44.fs_contigsumsize)); + printk(" contigsumsize: %d\n", SWAB32(usb3->fs_u2.fs_44.fs_contigsumsize)); printk(" postblformat: %u\n", SWAB32(usb3->fs_postblformat)); printk(" nrpos: %u\n", SWAB32(usb3->fs_nrpos)); printk(" ndir %u\n", SWAB32(usb1->fs_cstotal.cs_ndir)); @@ -269,10 +269,12 @@ static int ufs_parse_options (char * options, unsigned * mount_options) ufs_set_opt (*mount_options, UFSTYPE_SUN); else if (!strcmp (value, "44bsd")) ufs_set_opt (*mount_options, UFSTYPE_44BSD); - else if (!strcmp (value, "next")) - ufs_set_opt (*mount_options, UFSTYPE_NEXT); + else if (!strcmp (value, "nextstep")) + ufs_set_opt (*mount_options, UFSTYPE_NEXTSTEP); else if (!strcmp (value, "openstep")) ufs_set_opt (*mount_options, UFSTYPE_OPENSTEP); + else if (!strcmp (value, "sunx86")) + ufs_set_opt (*mount_options, UFSTYPE_SUNx86); else { printk ("UFS-fs: Invalid type option: %s\n", value); return 0; @@ -463,7 +465,7 @@ struct super_block * ufs_read_super (struct super_block * sb, void * data, } if (!(sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE)) { printk("You didn't specify the type of your ufs filesystem\n\n" - " mount -t ufs -o ufstype=sun|44bsd|old|next|openstep ....\n\n" + " mount -t ufs -o ufstype=sun|sunx86|44bsd|old|nextstep|openstep ....\n\n" ">>>WARNING<<< Wrong ufstype may corrupt your filesystem, " "default is ufstype=old\n"); ufs_set_opt (sb->u.ufs_sb.s_mount_opt, UFSTYPE_OLD); @@ -495,6 +497,16 @@ struct super_block * ufs_read_super (struct super_block * sb, void * data, flags |= UFS_DE_OLD | UFS_UID_EFT | UFS_ST_SUN | UFS_CG_SUN; break; + case UFS_MOUNT_UFSTYPE_SUNx86: + UFSD(("ufstype=sunx86\n")) + uspi->s_fsize = block_size = 1024; + uspi->s_fmask = ~(1024 - 1); + uspi->s_fshift = 10; + uspi->s_sbsize = super_block_size = 2048; + uspi->s_sbbase = 0; + flags |= UFS_DE_OLD | UFS_UID_EFT | UFS_ST_SUNx86 | UFS_CG_SUN; + break; + case UFS_MOUNT_UFSTYPE_OLD: UFSD(("ufstype=old\n")) uspi->s_fsize = block_size = 1024; @@ -509,8 +521,8 @@ struct super_block * ufs_read_super (struct super_block * sb, void * data, } break; - case UFS_MOUNT_UFSTYPE_NEXT: - UFSD(("ufstype=next\n")) + case UFS_MOUNT_UFSTYPE_NEXTSTEP: + UFSD(("ufstype=nextstep\n")) uspi->s_fsize = block_size = 1024; uspi->s_fmask = ~(1024 - 1); uspi->s_fshift = 10; @@ -518,7 +530,7 @@ struct super_block * ufs_read_super (struct super_block * sb, void * data, uspi->s_sbbase = 0; flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD; if (!(sb->s_flags & MS_RDONLY)) { - printk(KERN_INFO "ufstype=next is supported read-only\n"); + printk(KERN_INFO "ufstype=nextstep is supported read-only\n"); sb->s_flags |= MS_RDONLY; } break; @@ -579,10 +591,9 @@ again: } #endif - if ((((sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) == - UFS_MOUNT_UFSTYPE_NEXT) || - ((sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) == - UFS_MOUNT_UFSTYPE_OPENSTEP)) && uspi->s_sbbase < 256) { + if ((((sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) == UFS_MOUNT_UFSTYPE_NEXTSTEP) + || ((sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) == UFS_MOUNT_UFSTYPE_OPENSTEP)) + && uspi->s_sbbase < 256) { ubh_brelse_uspi(uspi); ubh = NULL; uspi->s_sbbase += 8; @@ -627,9 +638,10 @@ magic_found: * If not, make it read only. */ if (((flags & UFS_ST_MASK) == UFS_ST_44BSD) || - ((flags & UFS_ST_MASK) == UFS_ST_OLD) || - (((flags & UFS_ST_MASK) == UFS_ST_SUN) && - (ufs_get_fs_state(usb3) == (UFS_FSOK - SWAB32(usb1->fs_time))))) { + ((flags & UFS_ST_MASK) == UFS_ST_OLD) || + (((flags & UFS_ST_MASK) == UFS_ST_SUN || + (flags & UFS_ST_MASK) == UFS_ST_SUNx86) && + (ufs_get_fs_state(usb1, usb3) == (UFS_FSOK - SWAB32(usb1->fs_time))))) { switch(usb1->fs_clean) { case UFS_FSCLEAN: UFSD(("fs is clean\n")) @@ -649,8 +661,7 @@ magic_found: sb->s_flags |= MS_RDONLY; break; default: - printk("ufs_read_super: can't grok fs_clean 0x%x\n", - usb1->fs_clean); + printk("ufs_read_super: can't grok fs_clean 0x%x\n", usb1->fs_clean); sb->s_flags |= MS_RDONLY; break; } @@ -694,7 +705,7 @@ magic_found: uspi->s_nindir = SWAB32(usb1->fs_nindir); uspi->s_inopb = SWAB32(usb1->fs_inopb); uspi->s_nspf = SWAB32(usb1->fs_nspf); - uspi->s_npsect = SWAB32(usb1->fs_npsect); + uspi->s_npsect = ufs_get_fs_npsect(usb1, usb3); uspi->s_interleave = SWAB32(usb1->fs_interleave); uspi->s_trackskew = SWAB32(usb1->fs_trackskew); uspi->s_csaddr = SWAB32(usb1->fs_csaddr); @@ -706,7 +717,7 @@ magic_found: uspi->s_ipg = SWAB32(usb1->fs_ipg); uspi->s_fpg = SWAB32(usb1->fs_fpg); uspi->s_cpc = SWAB32(usb2->fs_cpc); - uspi->s_contigsumsize = SWAB32(usb3->fs_u.fs_44.fs_contigsumsize); + uspi->s_contigsumsize = SWAB32(usb3->fs_u2.fs_44.fs_contigsumsize); uspi->s_qbmask = ufs_get_fs_qbmask(usb3); uspi->s_qfmask = ufs_get_fs_qfmask(usb3); uspi->s_postblformat = SWAB32(usb3->fs_postblformat); @@ -734,8 +745,6 @@ magic_found: sb->u.ufs_sb.s_flags = flags; sb->u.ufs_sb.s_swab = swab; - sb->u.ufs_sb.s_rename_lock = 0; - sb->u.ufs_sb.s_rename_wait = NULL; sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL); @@ -776,8 +785,9 @@ void ufs_write_super (struct super_block * sb) { if (!(sb->s_flags & MS_RDONLY)) { usb1->fs_time = SWAB32(CURRENT_TIME); - if (usb1->fs_clean == UFS_FSCLEAN && (flags&UFS_ST_MASK) == UFS_ST_SUN) - ufs_set_fs_state(usb3, UFS_FSOK - SWAB32(usb1->fs_time)); + if ((flags & UFS_ST_MASK) == UFS_ST_SUN + || (flags & UFS_ST_MASK) == UFS_ST_SUNx86) + ufs_set_fs_state(usb1, usb3, UFS_FSOK - SWAB32(usb1->fs_time)); ubh_mark_buffer_dirty (USPI_UBH, 1); } sb->s_dirt = 0; @@ -842,8 +852,9 @@ int ufs_remount (struct super_block * sb, int * mount_flags, char * data) if (*mount_flags & MS_RDONLY) { ufs_put_cylinder_structures(sb); usb1->fs_time = SWAB32(CURRENT_TIME); - if (usb1->fs_clean == UFS_FSCLEAN && (flags&UFS_ST_MASK) == UFS_ST_SUN) - ufs_set_fs_state(usb3, UFS_FSOK - SWAB32(usb1->fs_time)); + if ((flags & UFS_ST_MASK) == UFS_ST_SUN + || (flags & UFS_ST_MASK) == UFS_ST_SUNx86) + ufs_set_fs_state(usb1, usb3, UFS_FSOK - SWAB32(usb1->fs_time)); ubh_mark_buffer_dirty (USPI_UBH, 1); sb->s_dirt = 0; sb->s_flags |= MS_RDONLY; diff --git a/fs/ufs/util.h b/fs/ufs/util.h index 02e6fe98a..cf26773ef 100644 --- a/fs/ufs/util.h +++ b/fs/ufs/util.h @@ -31,24 +31,46 @@ /* * macros used for accesing structures */ -#define ufs_get_fs_state(usb3) _ufs_get_fs_state_(usb3,flags,swab) -static inline __s32 _ufs_get_fs_state_(struct ufs_super_block_third * usb3, - unsigned flags, unsigned swab) +#define ufs_get_fs_state(usb1,usb3) _ufs_get_fs_state_(usb1,usb3,flags,swab) +static inline __s32 _ufs_get_fs_state_(struct ufs_super_block_first * usb1, + struct ufs_super_block_third * usb3, unsigned flags, unsigned swab) { - if ((flags & UFS_ST_MASK) == UFS_ST_SUN) - return SWAB32((usb3)->fs_u.fs_sun.fs_state); - else - return SWAB32((usb3)->fs_u.fs_44.fs_state); + switch (flags & UFS_ST_MASK) { + case UFS_ST_SUN: + return SWAB32((usb3)->fs_u2.fs_sun.fs_state); + case UFS_ST_SUNx86: + return SWAB32((usb1)->fs_u1.fs_sunx86.fs_state); + case UFS_ST_44BSD: + default: + return SWAB32((usb3)->fs_u2.fs_44.fs_state); + } +} + +#define ufs_set_fs_state(usb1,usb3,value) _ufs_set_fs_state_(usb1,usb3,value,flags,swab) +static inline void _ufs_set_fs_state_(struct ufs_super_block_first * usb1, + struct ufs_super_block_third * usb3, __s32 value, unsigned flags, unsigned swab) +{ + switch (flags & UFS_ST_MASK) { + case UFS_ST_SUN: + (usb3)->fs_u2.fs_sun.fs_state = SWAB32(value); + break; + case UFS_ST_SUNx86: + (usb1)->fs_u1.fs_sunx86.fs_state = SWAB32(value); + break; + case UFS_ST_44BSD: + (usb3)->fs_u2.fs_44.fs_state = SWAB32(value); + break; + } } -#define ufs_set_fs_state(usb3,value) _ufs_set_fs_state_(usb3,value,flags,swab) -static inline void _ufs_set_fs_state_(struct ufs_super_block_third * usb3, - __s32 value, unsigned flags, unsigned swab) +#define ufs_get_fs_npsect(usb1,usb3) _ufs_get_fs_npsect_(usb1,usb3,flags,swab) +static inline __u32 _ufs_get_fs_npsect_(struct ufs_super_block_first * usb1, + struct ufs_super_block_third * usb3, unsigned flags, unsigned swab) { - if ((flags & UFS_ST_MASK) == UFS_ST_SUN) - (usb3)->fs_u.fs_sun.fs_state = SWAB32(value); - else - (usb3)->fs_u.fs_44.fs_state = SWAB32(value); + if ((flags & UFS_ST_MASK) == UFS_ST_SUNx86) + return SWAB32((usb3)->fs_u2.fs_sunx86.fs_npsect); + else + return SWAB32((usb1)->fs_u1.fs_sun.fs_npsect); } #define ufs_get_fs_qbmask(usb3) _ufs_get_fs_qbmask_(usb3,flags,swab) @@ -56,13 +78,19 @@ static inline __u64 _ufs_get_fs_qbmask_(struct ufs_super_block_third * usb3, unsigned flags, unsigned swab) { __u64 tmp; - if ((flags & UFS_ST_MASK) == UFS_ST_SUN) { - ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qbmask[0]; - ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qbmask[1]; - } - else { - ((u32 *)&tmp)[0] = usb3->fs_u.fs_44.fs_qbmask[0]; - ((u32 *)&tmp)[1] = usb3->fs_u.fs_44.fs_qbmask[1]; + switch (flags & UFS_ST_MASK) { + case UFS_ST_SUN: + ((u32 *)&tmp)[0] = usb3->fs_u2.fs_sun.fs_qbmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u2.fs_sun.fs_qbmask[1]; + break; + case UFS_ST_SUNx86: + ((u32 *)&tmp)[0] = usb3->fs_u2.fs_sunx86.fs_qbmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u2.fs_sunx86.fs_qbmask[1]; + break; + case UFS_ST_44BSD: + ((u32 *)&tmp)[0] = usb3->fs_u2.fs_44.fs_qbmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u2.fs_44.fs_qbmask[1]; + break; } return SWAB64(tmp); } @@ -72,13 +100,19 @@ static inline __u64 _ufs_get_fs_qfmask_(struct ufs_super_block_third * usb3, unsigned flags, unsigned swab) { __u64 tmp; - if ((flags & UFS_ST_MASK) == UFS_ST_SUN) { - ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qfmask[0]; - ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qfmask[1]; - } - else { - ((u32 *)&tmp)[0] = usb3->fs_u.fs_44.fs_qfmask[0]; - ((u32 *)&tmp)[1] = usb3->fs_u.fs_44.fs_qfmask[1]; + switch (flags & UFS_ST_MASK) { + case UFS_ST_SUN: + ((u32 *)&tmp)[0] = usb3->fs_u2.fs_sun.fs_qfmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u2.fs_sun.fs_qfmask[1]; + break; + case UFS_ST_SUNx86: + ((u32 *)&tmp)[0] = usb3->fs_u2.fs_sunx86.fs_qfmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u2.fs_sunx86.fs_qfmask[1]; + break; + case UFS_ST_44BSD: + ((u32 *)&tmp)[0] = usb3->fs_u2.fs_44.fs_qfmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u2.fs_44.fs_qfmask[1]; + break; } return SWAB64(tmp); } diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index 6ab276ac4..799f685de 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -30,7 +30,7 @@ extern struct inode *pseudo_root; */ /* nothing for now ... */ -static int umsdos_dentry_validate(struct dentry *dentry) +static int umsdos_dentry_validate(struct dentry *dentry, int flags) { return 1; } @@ -46,7 +46,7 @@ static void umsdos_dentry_dput(struct dentry *dentry) struct dentry_operations umsdos_dentry_operations = { - umsdos_dentry_validate, /* d_validate(struct dentry *) */ + umsdos_dentry_validate, /* d_revalidate(struct dentry *, int) */ NULL, /* d_hash */ NULL, /* d_compare */ umsdos_dentry_dput, /* d_delete(struct dentry *) */ @@ -210,8 +210,8 @@ filp->f_dentry->d_name.name, entry.name); /* * Do a real lookup on the short name. */ - dret = umsdos_lookup_dentry(filp->f_dentry, info.fake.fname, - info.fake.len, 1); + dret = umsdos_covered(filp->f_dentry, info.fake.fname, + info.fake.len); ret = PTR_ERR(dret); if (IS_ERR(dret)) break; @@ -459,7 +459,7 @@ int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry) * entry from the EMD file, and return ENOENT. */ -int umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo) +struct dentry *umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo) { struct dentry *dret = NULL; struct inode *inode; @@ -500,8 +500,7 @@ Printk (("lookup %.*s pos %lu ret %d len %d ", info.fake.len, info.fake.fname, info.f_pos, ret, info.fake.len)); /* do a real lookup to get the short name ... */ - dret = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, - info.fake.len, 1); + dret = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len); ret = PTR_ERR(dret); if (IS_ERR(dret)) { printk("umsdos_lookup_x: %s/%s real lookup failed, ret=%d\n", @@ -563,7 +562,7 @@ out_dput: dput(dret); out: umsdos_endlookup (dir); - return ret; + return ERR_PTR(ret); out_remove: printk(KERN_WARNING "UMSDOS: entry %s/%s out of sync, erased\n", @@ -581,23 +580,48 @@ out_remove: * Called by VFS; should fill dentry->d_inode via d_add. */ -int UMSDOS_lookup (struct inode *dir, struct dentry *dentry) +struct dentry *UMSDOS_lookup (struct inode *dir, struct dentry *dentry) { - int ret; + struct dentry *ret; ret = umsdos_lookup_x (dir, dentry, 0); /* Create negative dentry if not found. */ - if (ret == -ENOENT) { + if (ret == ERR_PTR(-ENOENT)) { Printk ((KERN_DEBUG "UMSDOS_lookup: converting -ENOENT to negative\n")); d_add (dentry, NULL); dentry->d_op = &umsdos_dentry_operations; - ret = 0; + ret = NULL; } return ret; } +struct dentry *umsdos_covered(struct dentry *parent, char *name, int len) +{ + struct dentry *result, *dentry; + struct qstr qstr; + + qstr.name = name; + qstr.len = len; + qstr.hash = full_name_hash(name, len); + result = ERR_PTR(-ENOMEM); + dentry = d_alloc(parent, &qstr); + if (dentry) { + /* XXXXXXXXXXXXXXXXXXX Race alert! */ + result = UMSDOS_rlookup(parent->d_inode, dentry); + d_drop(dentry); + if (result) + goto out_fail; + return dentry; + } +out: + return result; + +out_fail: + dput(dentry); + goto out; +} /* * Lookup or create a dentry from within the filesystem. @@ -609,7 +633,6 @@ struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len, int real) { struct dentry *result, *dentry; - int error; struct qstr qstr; qstr.name = name; @@ -620,20 +643,19 @@ struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len, result = ERR_PTR(-ENOMEM); dentry = d_alloc(parent, &qstr); if (dentry) { - result = dentry; - error = real ? - UMSDOS_rlookup(parent->d_inode, result) : - UMSDOS_lookup(parent->d_inode, result); - if (error) + result = real ? + UMSDOS_rlookup(parent->d_inode, dentry) : + UMSDOS_lookup(parent->d_inode, dentry); + if (result) goto out_fail; + return dentry; } } out: return result; out_fail: - dput(result); - result = ERR_PTR(error); + dput(dentry); goto out; } @@ -706,11 +728,6 @@ hlink->d_parent->d_name.name, hlink->d_name.name, path); len = (int) (pt - start); if (*pt == '/') *pt++ = '\0'; - real = (dir->d_inode->u.umsdos_i.i_emd_dir == 0); - /* - * Hack alert! inode->u.umsdos_i.i_emd_dir isn't reliable, - * so just check whether there's an EMD file ... - */ real = 1; demd = umsdos_get_emd_dentry(dir); if (!IS_ERR(demd)) { @@ -720,9 +737,8 @@ hlink->d_parent->d_name.name, hlink->d_name.name, path); } #ifdef UMSDOS_DEBUG_VERBOSE -printk ("umsdos_solve_hlink: dir %s/%s, name=%s, emd_dir=%ld, real=%d\n", -dir->d_parent->d_name.name, dir->d_name.name, start, -dir->d_inode->u.umsdos_i.i_emd_dir, real); +printk ("umsdos_solve_hlink: dir %s/%s, name=%s, real=%d\n", +dir->d_parent->d_name.name, dir->d_name.name, start, real); #endif dentry_dst = umsdos_lookup_dentry(dir, start, len, real); if (real) diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c index 6a4e99c04..d5a7a9578 100644 --- a/fs/umsdos/emd.c +++ b/fs/umsdos/emd.c @@ -250,12 +250,8 @@ parent->d_name.name, demd->d_name.name)); printk (KERN_WARNING "umsdos_make_emd: create %s/%s failed, err=%d\n", parent->d_name.name, demd->d_name.name, err); - goto out_dput; } out_set: - parent->d_inode->u.umsdos_i.i_emd_dir = demd->d_inode->i_ino; - -out_dput: dput(demd); out: return err; diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index ef6e56dc3..c7c94b558 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -50,10 +50,10 @@ void fill_new_filp (struct file *filp, struct dentry *dentry) void UMSDOS_put_inode (struct inode *inode) { PRINTK ((KERN_DEBUG - "put inode %p (%lu) owner %lu pos %lu dir %lu count=%d\n" + "put inode %p (%lu) pos %lu count=%d\n" ,inode, inode->i_ino - ,inode->u.umsdos_i.i_emd_owner, inode->u.umsdos_i.pos - ,inode->u.umsdos_i.i_emd_dir, inode->i_count)); + ,inode->u.umsdos_i.pos + ,inode->i_count)); if (inode == pseudo_root) { printk (KERN_ERR "Umsdos: Oops releasing pseudo_root." @@ -94,7 +94,6 @@ void umsdos_setup_dir(struct dentry *dir) printk(KERN_ERR "umsdos_setup_dir: %s/%s not a dir!\n", dir->d_parent->d_name.name, dir->d_name.name); - inode->u.umsdos_i.i_emd_dir = 0; inode->i_op = &umsdos_rdir_inode_operations; if (umsdos_have_emd(dir)) { Printk((KERN_DEBUG "umsdos_setup_dir: %s/%s using EMD\n", @@ -112,14 +111,11 @@ void umsdos_set_dirinfo_new (struct dentry *dentry, off_t f_pos) struct inode *inode = dentry->d_inode; struct dentry *demd; - inode->u.umsdos_i.i_emd_owner = 0; inode->u.umsdos_i.pos = f_pos; /* now check the EMD file */ demd = umsdos_get_emd_dentry(dentry->d_parent); if (!IS_ERR(demd)) { - if (demd->d_inode) - inode->u.umsdos_i.i_emd_owner = demd->d_inode->i_ino; dput(demd); } return; @@ -149,7 +145,6 @@ PRINTK (("umsdos_patch_inode: call umsdos_set_dirinfo_new(%p,%lu)\n", dentry, f_pos)); umsdos_set_dirinfo_new(dentry, f_pos); - inode->u.umsdos_i.i_emd_dir = 0; if (S_ISREG (inode->i_mode)) { if (MSDOS_SB (inode->i_sb)->cvf_format) { if (MSDOS_SB (inode->i_sb)->cvf_format->flags & CVF_USE_READPAGE) { @@ -327,8 +322,6 @@ void UMSDOS_write_inode (struct inode *inode) { struct iattr newattrs; - PRINTK (("UMSDOS_write_inode emd %d (FIXME: missing notify_change)\n", - inode->u.umsdos_i.i_emd_owner)); fat_write_inode (inode); newattrs.ia_mtime = inode->i_mtime; newattrs.ia_atime = inode->i_atime; diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c index 8e7780395..444e9ffae 100644 --- a/fs/umsdos/namei.c +++ b/fs/umsdos/namei.c @@ -6,6 +6,10 @@ * * Maintain and access the --linux alternate directory file. */ + /* + * You are in the maze of twisted functions - half of them shouldn't + * be here... + */ #include <linux/errno.h> #include <linux/kernel.h> @@ -171,18 +175,6 @@ void umsdos_endlookup (struct inode *dir) #endif -/* - * Check whether we can delete from the directory. - */ -static int is_sticky(struct inode *dir, int uid) -{ - return !((dir->i_mode & S_ISVTX) == 0 || - current->fsuid == uid || - current->fsuid == dir->i_uid || - capable (CAP_FOWNER)); -} - - static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry, int errcod) { @@ -258,16 +250,11 @@ static int umsdos_create_any (struct inode *dir, struct dentry *dentry, goto out; /* do a real lookup to get the short name dentry */ - fake = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, - info.fake.len, 1); + fake = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len); ret = PTR_ERR(fake); if (IS_ERR(fake)) goto out_remove; - /* keep the short name anonymous ... */ - if (dentry != fake) - d_drop(fake); - /* should not exist yet ... */ ret = -EEXIST; if (fake->d_inode) @@ -278,14 +265,8 @@ static int umsdos_create_any (struct inode *dir, struct dentry *dentry, goto out_remove_dput; inode = fake->d_inode; - /* - * Note! The long and short name might be the same, - * so check first before doing the instantiate ... - */ - if (dentry != fake) { - inode->i_count++; - d_instantiate (dentry, inode); - } + inode->i_count++; + d_instantiate (dentry, inode); dput(fake); if (inode->i_count > 1) { printk(KERN_WARNING @@ -363,7 +344,7 @@ static int umsdos_rename_f (struct inode *old_dir, struct dentry *old_dentry, { struct inode *old_inode = old_dentry->d_inode; struct dentry *old, *new, *old_emd; - int err, ret, rehash = 0; + int err, ret; struct umsdos_info old_info; struct umsdos_info new_info; @@ -386,34 +367,11 @@ static int umsdos_rename_f (struct inode *old_dir, struct dentry *old_dentry, umsdos_lockcreate2 (old_dir, new_dir); ret = umsdos_findentry(old_emd->d_parent, &old_info, 0); - if (ret) { - printk(KERN_ERR - "umsdos_rename_f: old entry %s/%s not in EMD, ret=%d\n", - old_dentry->d_parent->d_name.name, old_info.entry.name, - ret); - goto out_unlock; - } - - /* check sticky bit on old_dir */ - ret = -EPERM; - if (is_sticky(old_dir, old_info.entry.uid)) { -printk("umsdos_rename_f: %s/%s old sticky bit, fsuid=%d, uid=%d, dir=%d\n", -old_dentry->d_parent->d_name.name, old_info.entry.name, -current->fsuid, old_info.entry.uid, old_dir->i_uid); + if (ret) goto out_unlock; - } - /* - * Check whether the new_name already exists, and - * if so whether we're allowed to replace it. - */ err = umsdos_findentry(new_dentry->d_parent, &new_info, 0); if (err == 0) { - /* Are we allowed to replace it? */ - if (is_sticky(new_dir, new_info.entry.uid)) { -Printk (("sticky set on new ")); - goto out_unlock; - } /* check whether it _really_ exists ... */ ret = -EEXIST; if (new_dentry->d_inode) @@ -427,108 +385,41 @@ Printk (("sticky set on new ")); S_ISDIR(new_info.entry.mode)); } -Printk (("new newentry ")); - /* create the new entry ... */ umsdos_ren_init (&new_info, &old_info); if (flags) new_info.entry.flags = flags; ret = umsdos_newentry (new_dentry->d_parent, &new_info); - if (ret) { - printk(KERN_WARNING - "umsdos_rename_f: newentry %s/%s failed, ret=%d\n", - new_dentry->d_parent->d_name.name, new_info.entry.name, - ret); + if (ret) goto out_unlock; - } /* If we're moving a hardlink, drop it first */ if (old_info.entry.flags & UMSDOS_HLINK) { - rehash = !list_empty(&old_dentry->d_hash); d_drop(old_dentry); -printk("umsdos_rename_f: moving hardlink %s/%s, rehash=%d\n", -old_dentry->d_parent->d_name.name, old_info.entry.name, rehash); } - /* Do a real lookup to get the old short name dentry */ - old = umsdos_lookup_dentry(old_dentry->d_parent, old_info.fake.fname, - old_info.fake.len, 1); + old = umsdos_covered(old_dentry->d_parent, old_info.fake.fname, + old_info.fake.len); ret = PTR_ERR(old); - if (IS_ERR(old)) { - printk(KERN_WARNING - "umsdos_rename_f: lookup old dentry %s/%s, ret=%d\n", - old_dentry->d_parent->d_name.name, old_info.fake.fname, - ret); + if (IS_ERR(old)) goto out_unlock; - } - /* short and long name dentries match? */ - if (old == old_dentry) - dput(old); - else { - /* make sure it's the same inode! */ - ret = -ENOENT; - if (old->d_inode != old_inode) - goto out_dput; - /* - * A cross-directory move with different short and long - * names has nasty complications: msdos-fs will need to - * change inodes, so we must check whether the original - * dentry is busy, and if the rename succeeds the short - * dentry will come back with a different inode. - * - * To handle this, we drop the dentry and free the inode, - * and then pick up the new inode after the rename. - */ - if (old_dir != new_dir) { - ret = -EBUSY; - if (old_dentry->d_count > 1) { -printk("umsdos_rename_f: old dentry %s/%s busy, d_count=%d\n", -old_dentry->d_parent->d_name.name, old_dentry->d_name.name,old_dentry->d_count); - goto out_dput; - } - d_drop(old_dentry); - d_delete(old_dentry); -printk("umsdos_rename_f: cross-dir move, %s/%s dropped\n", -old_dentry->d_parent->d_name.name, old_dentry->d_name.name); - } - } + /* make sure it's the same inode! */ + ret = -ENOENT; + if (old->d_inode != old_inode) + goto out_dput; - new = umsdos_lookup_dentry(new_dentry->d_parent, new_info.fake.fname, - new_info.fake.len, 1); + new = umsdos_covered(new_dentry->d_parent, new_info.fake.fname, + new_info.fake.len); ret = PTR_ERR(new); - if (IS_ERR(new)) { - printk(KERN_WARNING - "umsdos_rename_f: lookup new dentry %s/%s, ret=%d\n", - new_dentry->d_parent->d_name.name, new_info.fake.fname, - ret); + if (IS_ERR(new)) goto out_dput; - } -#ifdef UMSDOS_PARANOIA -if (new->d_inode != new_dentry->d_inode) -printk("umsdos_rename_f: new %s/%s, inode %p!=%p??\n", -new->d_parent->d_name.name, new->d_name.name, new->d_inode,new_dentry->d_inode); -#endif - /* short and long name dentries match? */ - if (new == new_dentry) - dput(new); -#ifdef UMSDOS_DEBUG_VERBOSE -printk("umsdos_rename_f: msdos_rename %s/%s(%ld) to %s/%s(%ld)\n", -old->d_parent->d_name.name, old->d_name.name, old->d_inode->i_ino, -new->d_parent->d_name.name, new->d_name.name, -new->d_inode ? new->d_inode->i_ino : 0); -#endif /* Do the msdos-level rename */ ret = msdos_rename (old_dir, old, new_dir, new); -Printk(("umsdos_rename_f: now %s/%s, ret=%d\n", -old->d_parent->d_name.name, old->d_name.name, ret)); - if (new != new_dentry) - dput(new); + dput(new); /* If the rename failed, remove the new EMD entry */ if (ret != 0) { -Printk(("umsdos_rename_f: rename failed, ret=%d, removing %s/%s\n", -ret, new_dentry->d_parent->d_name.name, new_info.entry.name)); umsdos_delentry (new_dentry->d_parent, &new_info, S_ISDIR (new_info.entry.mode)); goto out_dput; @@ -550,33 +441,6 @@ ret, new_dentry->d_parent->d_name.name, new_info.entry.name)); } /* - * Check whether to update the dcache ... if both - * old and new dentries match, it's already correct. - * If the targets were aliases, the old short-name - * dentry has the original target name. - */ - if (old_dentry != old) { - if (!old_dentry->d_inode) { - struct inode *inode = old->d_inode; - inode->i_count++; - d_instantiate(old_dentry, inode); -printk("umsdos_rename_f: %s/%s gets new ino=%ld\n", -old_dentry->d_parent->d_name.name, old_dentry->d_name.name, inode->i_ino); - } - if (new_dentry == new) - new_dentry = old; - goto move_it; - } else if (new_dentry != new) { - move_it: - /* this will rehash the dentry ... */ - d_move(old_dentry, new_dentry); - } - /* Check whether the old inode changed ... */ - if (old_dentry->d_inode != old_inode) { - umsdos_lookup_patch_new(old_dentry, &new_info); - } - - /* * Update f_pos so notify_change will succeed * if the file was already in use. */ @@ -584,8 +448,7 @@ old_dentry->d_parent->d_name.name, old_dentry->d_name.name, inode->i_ino); /* dput() the dentry if we haven't already */ out_dput: - if (old_dentry != old) - dput(old); + dput(old); out_unlock: dput(old_emd); @@ -614,6 +477,9 @@ out: extern struct inode_operations umsdos_symlink_inode_operations; +/* + * AV. Should be called with dir->i_sem down. + */ static int umsdos_symlink_x (struct inode *dir, struct dentry *dentry, const char *symname, int mode, char flags) { @@ -745,8 +611,9 @@ dentry->d_parent->d_name.name, hid_info.entry.name, ret); goto cleanup; } /* rename the link to the hidden location ... */ - ret = umsdos_rename_f (olddir, olddentry, olddir, temp, + ret = umsdos_rename_f(olddir, olddentry, olddir, temp, UMSDOS_HIDDEN); + d_move(olddentry, temp); dput(temp); if (ret) { printk("umsdos_link: rename to %s/%s failed, ret=%d\n", @@ -817,9 +684,8 @@ Printk(("UMSDOS_link: hard link %s/%s, fake=%s\n", olddentry->d_parent->d_name.name, olddentry->d_name.name, old_info.fake.fname)); /* Do a real lookup to get the short name dentry */ - temp = umsdos_lookup_dentry(olddentry->d_parent, - old_info.fake.fname, - old_info.fake.len, 1); + temp = umsdos_covered(olddentry->d_parent, old_info.fake.fname, + old_info.fake.len); ret = PTR_ERR(temp); if (IS_ERR(temp)) goto out_unlock; @@ -905,16 +771,11 @@ int UMSDOS_mkdir (struct inode *dir, struct dentry *dentry, int mode) goto out; /* lookup the short name dentry */ - temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, - info.fake.len, 1); + temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len); ret = PTR_ERR(temp); if (IS_ERR(temp)) goto out_remove; - /* Keep the short name dentry anonymous */ - if (temp != dentry) - d_drop(temp); - /* Make sure the short name doesn't exist */ ret = -EEXIST; if (temp->d_inode) { @@ -933,16 +794,9 @@ dentry->d_parent->d_name.name, info.fake.fname); inode = temp->d_inode; down(&inode->i_sem); - /* - * Note! The long and short name might be the same, - * so check first before doing the instantiate ... - */ - if (dentry != temp) { -if (dentry->d_inode) -printk("umsdos_mkdir: dentry not negative!\n"); - inode->i_count++; - d_instantiate(dentry, inode); - } + inode->i_count++; + d_instantiate(dentry, inode); + /* N.B. this should have an option to create the EMD ... */ umsdos_lookup_patch_new(dentry, &info); @@ -1042,21 +896,11 @@ demd->d_parent->d_name.name, demd->d_name.name, err); umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); /* Call findentry to complete the mangling */ umsdos_findentry (dentry->d_parent, &info, 2); - temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, - info.fake.len, 1); + temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len); ret = PTR_ERR(temp); if (IS_ERR(temp)) goto out; /* - * If the short name is an alias, dput() it now; - * otherwise d_drop() it to keep it anonymous. - */ - if (temp == dentry) - dput(temp); - else - d_drop(temp); - - /* * Attempt to remove the msdos name. */ ret = msdos_rmdir (dir, temp); @@ -1072,8 +916,7 @@ printk("umsdos_rmdir: delentry %s failed, ret=%d\n", info.entry.name, ret); /* dput() temp if we didn't do it above */ out_dput: - if (temp != dentry) - dput(temp); + dput(temp); out: Printk (("umsdos_rmdir %d\n", ret)); @@ -1096,7 +939,7 @@ int UMSDOS_unlink (struct inode *dir, struct dentry *dentry) { struct dentry *temp, *link = NULL; struct inode *inode; - int ret, rehash = 0; + int ret; struct umsdos_info info; Printk(("UMSDOS_unlink: entering %s/%s\n", @@ -1120,14 +963,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name, ret); Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname)); - ret = -EPERM; - /* check sticky bit */ - if (is_sticky(dir, info.entry.uid)) { -printk("umsdos_unlink: %s/%s is sticky\n", -dentry->d_parent->d_name.name, dentry->d_name.name); - goto out_unlock; - } - /* * Note! If this is a hardlink and the names are aliased, * the short-name lookup will return the hardlink dentry. @@ -1135,15 +970,11 @@ dentry->d_parent->d_name.name, dentry->d_name.name); * the original dentry. */ if (info.entry.flags & UMSDOS_HLINK) { - rehash = !list_empty(&dentry->d_hash); d_drop(dentry); -Printk(("UMSDOS_unlink: hard link %s/%s, fake=%s, rehash=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname, rehash)); } /* Do a real lookup to get the short name dentry */ - temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, - info.fake.len, 1); + temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len); ret = PTR_ERR(temp); if (IS_ERR(temp)) goto out_unlock; @@ -1155,13 +986,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname, rehash)); link = umsdos_solve_hlink(dget(temp)); } - /* - * If the short and long names are aliased, - * dput() it now so the dentry isn't busy. - */ - if (temp == dentry) - dput(temp); - /* Delete the EMD entry */ ret = umsdos_delentry (dentry->d_parent, &info, 0); if (ret && ret != -ENOENT) { @@ -1170,7 +994,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname, rehash)); goto out_dput; } - ret = msdos_unlink_umsdos (dir, temp); + ret = msdos_unlink(dir, temp); #ifdef UMSDOS_PARANOIA if (ret) printk("umsdos_unlink: %s/%s unlink failed, ret=%d\n", @@ -1179,12 +1003,9 @@ temp->d_parent->d_name.name, temp->d_name.name, ret); /* dput() temp if we didn't do it above */ out_dput: - if (temp != dentry) { - d_drop(temp); - dput(temp); - if (!ret) - d_delete (dentry); - } + dput(temp); + if (!ret) + d_delete (dentry); out_unlock: umsdos_unlockcreate (dir); @@ -1247,57 +1068,31 @@ out: return ret; } - /* * Rename (move) a file. */ int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { - struct dentry *new_target; int ret; -#ifdef UMSDOS_DEBUG_VERBOSE -printk("umsdos_rename: enter, %s/%s(%ld) to %s/%s(%ld)\n", -old_dentry->d_parent->d_name.name, old_dentry->d_name.name, -old_dentry->d_inode->i_ino, -new_dentry->d_parent->d_name.name, new_dentry->d_name.name, -new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0); -#endif ret = umsdos_nevercreat (new_dir, new_dentry, -EEXIST); if (ret) - goto out; + return ret; - /* - * If the target already exists, delete it first. - */ + /* + * If the target already exists, delete it first. + */ if (new_dentry->d_inode) { - if (S_ISDIR(new_dentry->d_inode->i_mode)) + new_dentry->d_count++; + if (S_ISDIR(old_dentry->d_inode->i_mode)) ret = UMSDOS_rmdir (new_dir, new_dentry); else ret = UMSDOS_unlink (new_dir, new_dentry); + dput(new_dentry); if (ret) - goto out; + return ret; } - - /* - * If we didn't get a negative dentry, make a copy and hash it. - */ - new_target = new_dentry; - if (new_dentry->d_inode) { -printk("umsdos_rename: %s/%s not negative, hash=%d\n", -new_dentry->d_parent->d_name.name, new_dentry->d_name.name, -!list_empty(&new_dentry->d_hash)); - ret = -ENOMEM; - new_target = d_alloc(new_dentry->d_parent, &new_dentry->d_name); - if (!new_target) - goto out; - d_add(new_target, NULL); - } - ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_target, 0); - if (new_target != new_dentry) - dput(new_target); - -out: + ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0); return ret; } diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c index 10ff145b7..3f5d10953 100644 --- a/fs/umsdos/rdir.c +++ b/fs/umsdos/rdir.c @@ -79,9 +79,9 @@ static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir) * In the real root directory (c:\), the directory .. * is the pseudo root (c:\linux). */ -int umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo) +struct dentry *umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo) { - int ret; + struct dentry *ret; if (saved_root && dir == saved_root->d_inode && !nopseudo && dentry->d_name.len == UMSDOS_PSDROOT_LEN && @@ -91,15 +91,16 @@ int umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo) * /linux won't show */ - ret = -ENOENT; + ret = ERR_PTR(-ENOENT); goto out; } ret = msdos_lookup (dir, dentry); if (ret) { printk(KERN_WARNING - "umsdos_rlookup_x: %s/%s failed, ret=%d\n", - dentry->d_parent->d_name.name, dentry->d_name.name,ret); + "umsdos_rlookup_x: %s/%s failed, ret=%ld\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + PTR_ERR(ret)); goto out; } if (dentry->d_inode) { @@ -119,7 +120,7 @@ out: } -int UMSDOS_rlookup ( struct inode *dir, struct dentry *dentry) +struct dentry *UMSDOS_rlookup ( struct inode *dir, struct dentry *dentry) { return umsdos_rlookup_x (dir, dentry, 0); } diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index d832bd99c..f79ef1b44 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -71,7 +71,7 @@ static int vfat_hashi(struct dentry *parent, struct qstr *qstr); static int vfat_hash(struct dentry *parent, struct qstr *qstr); static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b); static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b); -static int vfat_revalidate(struct dentry *dentry); +static int vfat_revalidate(struct dentry *dentry, int); static struct dentry_operations vfat_dentry_ops[4] = { { @@ -106,7 +106,7 @@ void vfat_put_super(struct super_block *sb) MOD_DEC_USE_COUNT; } -static int vfat_revalidate(struct dentry *dentry) +static int vfat_revalidate(struct dentry *dentry, int flags) { PRINTK1(("vfat_revalidate: %s\n", dentry->d_name.name)); if (dentry->d_time == dentry->d_parent->d_inode->i_version) { @@ -506,10 +506,10 @@ static int vfat_find_form(struct inode *dir,char *name) continue; if (memcmp(de->name,name,MSDOS_NAME)) continue; - brelse(bh); + fat_brelse(dir->i_sb,bh); return 0; } - brelse(bh); + fat_brelse(dir->i_sb,bh); return -ENOENT; } @@ -1140,11 +1140,30 @@ cleanup: return res; } -int vfat_lookup(struct inode *dir,struct dentry *dentry) +/* Find a hashed dentry for inode; NULL if there are none */ +static struct dentry *find_alias(struct inode *inode) +{ + struct list_head *head, *next, *tmp; + struct dentry *alias; + + head = &inode->i_dentry; + next = inode->i_dentry.next; + while (next != head) { + tmp = next; + next = tmp->next; + alias = list_entry(tmp, struct dentry, d_alias); + if (!list_empty(&alias->d_hash)) + return dget(alias); + } + return NULL; +} + +struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry) { int res; struct vfat_slot_info sinfo; struct inode *result; + struct dentry *alias; int table; PRINTK2(("vfat_lookup: name=%s, len=%d\n", @@ -1161,7 +1180,7 @@ int vfat_lookup(struct inode *dir,struct dentry *dentry) } PRINTK3(("vfat_lookup 4.5\n")); if (!(result = iget(dir->i_sb,sinfo.ino))) - return -EACCES; + return ERR_PTR(-EACCES); PRINTK3(("vfat_lookup 5\n")); if (MSDOS_I(result)->i_busy) { /* mkdir in progress */ iput(result); @@ -1169,6 +1188,15 @@ int vfat_lookup(struct inode *dir,struct dentry *dentry) table++; goto error; } + alias = find_alias(result); + if (alias) { + if (d_invalidate(alias)==0) + dput(alias); + else { + iput(result); + return alias; + } + } PRINTK3(("vfat_lookup 6\n")); error: dentry->d_op = &vfat_dentry_ops[table]; @@ -1397,28 +1425,6 @@ static int vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo, return 0; } -/* Drop all aliases */ -static void drop_aliases(struct dentry *dentry) -{ - struct list_head *head, *next, *tmp; - struct dentry *alias; - - PRINTK1(("drop_replace_inodes: dentry=%p, inode=%p\n", dentry, inode)); - head = &dentry->d_inode->i_dentry; - if (dentry->d_inode) { - next = dentry->d_inode->i_dentry.next; - while (next != head) { - tmp = next; - next = tmp->next; - alias = list_entry(tmp, struct dentry, d_alias); - if (alias == dentry) - continue; - - d_drop(alias); - } - } -} - static int vfat_rmdirx(struct inode *dir,struct dentry* dentry) { int res; @@ -1430,12 +1436,6 @@ static int vfat_rmdirx(struct inode *dir,struct dentry* dentry) if (res >= 0 && sinfo.total_slots > 0) { if (!list_empty(&dentry->d_hash)) return -EBUSY; - /* Take care of aliases */ - if (dentry->d_inode->i_count > 1) { - shrink_dcache_parent(dentry->d_parent); - if (dentry->d_inode->i_count > 1) - return -EBUSY; - } res = vfat_empty(dentry->d_inode); if (res) return res; @@ -1535,10 +1535,8 @@ int vfat_unlink(struct inode *dir,struct dentry* dentry) PRINTK1(("vfat_unlink: dentry=%p, inode=%p\n", dentry, dentry->d_inode)); res = vfat_unlinkx (dir,dentry,1); - if (res >= 0) { - drop_aliases(dentry); + if (res >= 0) d_delete(dentry); - } return res; } @@ -1561,7 +1559,6 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, loff_t old_offset,new_offset,old_longname_offset; int old_slots,old_ino,new_ino,dotdot_ino; struct inode *old_inode, *new_inode, *dotdot_inode; - struct dentry *walk; int res, is_dir, i; int locked = 0; struct vfat_slot_info sinfo; @@ -1570,14 +1567,6 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, old_dentry, old_dentry->d_inode, old_dentry->d_inode->i_ino, new_dentry, new_dentry->d_inode, new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0)); - /* - * POSIX is braindead (surprise, surprise). It requires that rename() - * should return 0 and do nothing if the target has the same inode as - * the source. Somebody, get a time machine, return to '89 and tell - * RMS & Co *not* to do that idiocy, FAST! - */ - if (old_dentry->d_inode == new_dentry->d_inode) - return 0; old_bh = new_bh = NULL; old_inode = new_inode = NULL; @@ -1597,24 +1586,8 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, old_inode = old_dentry->d_inode; is_dir = S_ISDIR(old_inode->i_mode); - /* - * Race: we can be hit by another rename after this check. - * For the time being use fat_lock_creation(), but it's - * ugly. FIXME. - */ - fat_lock_creation(); locked = 1; - if (is_dir) { - /* We can't use d_subdir() here. Arrgh. */ - for (walk=new_dentry;walk!=walk->d_parent;walk=walk->d_parent) { - if (walk->d_inode != old_dentry->d_inode) - continue; - res = -EINVAL; - goto rename_done; - } - } - if (new_dentry->d_inode) { /* * OK, we have to remove the target. We should do it so @@ -1634,31 +1607,16 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, } if (is_dir) { - /* - * Target is a directory. No other owners will - * be tolerated. - */ - res = -EBUSY; - if (d_invalidate(new_dentry) < 0) - goto rename_done; - /* - * OK, let's try to get rid of other dentries. - * No need to do it if i_count is 1. - */ - if (new_inode->i_count>1) { - shrink_dcache_parent(new_dentry->d_parent); - if (new_inode->i_count>1) - goto rename_done; - } res = vfat_empty(new_inode); if (res) goto rename_done; - } else { - drop_aliases(new_dentry); } res = vfat_remove_entry(new_dir,&sinfo,new_inode); if (res) goto rename_done; + + if (is_dir) + new_dir->i_nlink--; } /* Serious lossage here. FAT uses braindead inode numbers scheme, @@ -1670,9 +1628,6 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, * 'busy' stuff on the spot. Later. */ - if (is_dir) - new_dir->i_nlink--; - res = vfat_find(new_dir,&new_dentry->d_name,1,is_dir,&sinfo); if (res < 0) goto rename_done; @@ -1726,12 +1681,8 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, fat_brelse(sb, dotdot_bh); } - if (res >= 0) { - if (new_inode && is_dir) - d_rehash(new_dentry); - d_move(old_dentry, new_dentry); + if (res >= 0) res = 0; - } rename_done: if (locked) |