From f9bbe9da79dbc8557c74efeb158b431cd67ace52 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Tue, 3 Mar 1998 01:22:27 +0000 Subject: Upgrade to 2.1.73. --- fs/dcache.c | 36 ++++ fs/ext2/fsync.c | 197 ++++++++++++++++++- fs/fat/inode.c | 10 + fs/isofs/dir.c | 28 +-- fs/isofs/inode.c | 58 ++++-- fs/isofs/rock.c | 1 + fs/msdos/namei.c | 101 +++++----- fs/namei.c | 25 ++- fs/nfs/dir.c | 75 ++++++-- fs/nfs/inode.c | 16 +- fs/nfs/write.c | 7 +- fs/nfsd/vfs.c | 141 ++++++++------ fs/proc/generic.c | 2 +- fs/smbfs/dir.c | 174 ++++++++++------- fs/smbfs/file.c | 4 +- fs/smbfs/inode.c | 138 ++++++++----- fs/smbfs/ioctl.c | 21 +- fs/smbfs/proc.c | 563 ++++++++++++++++++++++++++++-------------------------- fs/smbfs/sock.c | 26 ++- fs/sysv/namei.c | 38 ++-- 20 files changed, 1057 insertions(+), 604 deletions(-) (limited to 'fs') diff --git a/fs/dcache.c b/fs/dcache.c index a2aaeeaec..7d41b0bd5 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -744,6 +744,42 @@ char * d_path(struct dentry *dentry, char *buffer, int buflen) return retval; } +/* + * Check whether a dentry already exists for the given name, + * and return the inode number if it has an inode. + * + * This routine is used to post-process directory listings for + * filesystems using synthetic inode numbers, and is necessary + * to keep getcwd() working. + */ +ino_t find_inode_number(struct dentry *dir, struct qstr *name) +{ + struct dentry * dentry; + ino_t ino = 0; + + /* + * Check for a fs-specific hash function. Note that we must + * calculate the standard hash first, as the d_op->d_hash() + * routine may choose to leave the hash value unchanged. + */ + name->hash = full_name_hash(name->name, name->len); + if (dir->d_op && dir->d_op->d_hash) + { + if (dir->d_op->d_hash(dir, name) != 0) + goto out; + } + + dentry = d_lookup(dir, name); + if (dentry) + { + if (dentry->d_inode) + ino = dentry->d_inode->i_ino; + dput(dentry); + } +out: + return ino; +} + __initfunc(void dcache_init(void)) { int i; diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c index b5b531d8c..8a9bdf902 100644 --- a/fs/ext2/fsync.c +++ b/fs/ext2/fsync.c @@ -57,6 +57,72 @@ static int sync_block (struct inode * inode, u32 * block, int wait) return 0; } +#ifndef __LITTLE_ENDIAN +static int sync_block_swab32 (struct inode * inode, u32 * block, int wait) +{ + struct buffer_head * bh; + + if (!le32_to_cpu(*block)) + return 0; + bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize); + if (!bh) + return 0; + if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { + brelse (bh); + return -1; + } + if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { + brelse (bh); + return 0; + } + ll_rw_block (WRITE, 1, &bh); + bh->b_count--; + return 0; +} +#else +#define sync_block_swab32 sync_block +#endif + + +static int sync_iblock (struct inode * inode, u32 * iblock, + struct buffer_head ** bh, int wait) +{ + int rc, tmp; + + *bh = NULL; + tmp = *iblock; + if (!tmp) + return 0; + rc = sync_block (inode, iblock, wait); + if (rc) + return rc; + *bh = bread (inode->i_dev, tmp, blocksize); + if (!*bh) + return -1; + return 0; +} + +#ifndef __LITTLE_ENDIAN +static int sync_iblock_swab32 (struct inode * inode, u32 * iblock, + struct buffer_head ** bh, int wait) +{ + int rc, tmp; + + *bh = NULL; + tmp = le32_to_cpu(*iblock); + if (!tmp) + return 0; + rc = sync_block_swab32 (inode, iblock, wait); + if (rc) + return rc; + *bh = bread (inode->i_dev, tmp, blocksize); + if (!*bh) + return -1; + return 0; +} +#else +#define sync_iblock_swab32 sync_iblock +#endif static int sync_direct (struct inode * inode, int wait) { @@ -71,15 +137,122 @@ static int sync_direct (struct inode * inode, int wait) return err; } +static int sync_indirect (struct inode * inode, u32 * iblock, int wait) +{ + int i; + struct buffer_head * ind_bh; + int rc, err = 0; + + rc = sync_iblock (inode, iblock, &ind_bh, wait); + if (rc || !ind_bh) + return rc; + + for (i = 0; i < addr_per_block; i++) { + rc = sync_block_swab32 (inode, + ((u32 *) ind_bh->b_data) + i, + wait); + if (rc) + err = rc; + } + brelse (ind_bh); + return err; +} + +#ifndef __LITTLE_ENDIAN +static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock, int wait) +{ + int i; + struct buffer_head * ind_bh; + int rc, err = 0; + + rc = sync_iblock_swab32 (inode, iblock, &ind_bh, wait); + if (rc || !ind_bh) + return rc; + + for (i = 0; i < addr_per_block; i++) { + rc = sync_block_swab32 (inode, + ((u32 *) ind_bh->b_data) + i, + wait); + if (rc) + err = rc; + } + brelse (ind_bh); + return err; +} +#else +#define sync_indirect_swab32 sync_indirect +#endif + +static int sync_dindirect (struct inode * inode, u32 * diblock, int wait) +{ + int i; + struct buffer_head * dind_bh; + int rc, err = 0; + + rc = sync_iblock (inode, diblock, &dind_bh, wait); + if (rc || !dind_bh) + return rc; + + for (i = 0; i < addr_per_block; i++) { + rc = sync_indirect_swab32 (inode, + ((u32 *) dind_bh->b_data) + i, + wait); + if (rc) + err = rc; + } + brelse (dind_bh); + return err; +} + +#ifndef __LITTLE_ENDIAN +static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock, int wait) +{ + int i; + struct buffer_head * dind_bh; + int rc, err = 0; + + rc = sync_iblock_swab32 (inode, diblock, &dind_bh, wait); + if (rc || !dind_bh) + return rc; + + for (i = 0; i < addr_per_block; i++) { + rc = sync_indirect_swab32 (inode, + ((u32 *) dind_bh->b_data) + i, + wait); + if (rc) + err = rc; + } + brelse (dind_bh); + return err; +} +#else +#define sync_dindirect_swab32 sync_dindirect +#endif + +static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait) +{ + int i; + struct buffer_head * tind_bh; + int rc, err = 0; + + rc = sync_iblock (inode, tiblock, &tind_bh, wait); + if (rc || !tind_bh) + return rc; + + for (i = 0; i < addr_per_block; i++) { + rc = sync_dindirect_swab32 (inode, + ((u32 *) tind_bh->b_data) + i, + wait); + if (rc) + err = rc; + } + brelse (tind_bh); + return err; +} + /* * File may be NULL when we are called. Perhaps we shouldn't * even pass file to fsync ? - * - * This currently falls back to synching the whole device when - * the file is larger than can fit directly in the inode. This - * is because dirty-buffer handling is indexed by the device - * of the buffer, which makes it much faster to sync the whole - * device than to sync just one large file. */ int ext2_sync_file(struct file * file, struct dentry *dentry) @@ -96,12 +269,18 @@ int ext2_sync_file(struct file * file, struct dentry *dentry) */ goto skip; - if (inode->i_size > EXT2_NDIR_BLOCKS*blocksize) - return file_fsync(file, dentry); - for (wait=0; wait<=1; wait++) { err |= sync_direct (inode, wait); + err |= sync_indirect (inode, + inode->u.ext2_i.i_data+EXT2_IND_BLOCK, + wait); + err |= sync_dindirect (inode, + inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, + wait); + err |= sync_tindirect (inode, + inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, + wait); } skip: err |= ext2_sync_inode (inode); diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 732d0b9ba..a907785f3 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -117,6 +117,16 @@ inode->i_ino, inode->i_count); void fat_delete_inode(struct inode *inode) { + /* + * Make sure there are no active dependencies ... + */ + if (MSDOS_I(inode)->i_old) + printk("fat_delete_inode: inode %ld, old=%p??\n", + inode->i_ino, MSDOS_I(inode)->i_old); + if (MSDOS_I(inode)->i_oldlink) + printk("fat_delete_inode: inode %ld, oldlink=%p??\n", + inode->i_ino, MSDOS_I(inode)->i_oldlink); + fat_cache_inval_inode(inode); inode->i_size = 0; fat_truncate(inode); diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 67ff77bd9..79d49a858 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -207,22 +207,22 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, } } + map = 1; + if (inode->i_sb->u.isofs_sb.s_rock) { + len = get_rock_ridge_filename(de, tmpname, inode); + if (len != 0) { + p = tmpname; + map = 0; + } + } + if (map) { #ifdef CONFIG_JOLIET - if (inode->i_sb->u.isofs_sb.s_joliet_level) { - len = get_joliet_filename(de, inode, tmpname); - p = tmpname; - } else + if (inode->i_sb->u.isofs_sb.s_joliet_level) { + len = get_joliet_filename(de, inode, tmpname); + p = tmpname; + } else #endif - /* if not joliet */ { - map = 1; - if (inode->i_sb->u.isofs_sb.s_rock) { - len = get_rock_ridge_filename(de, tmpname, inode); - if (len != 0) { - p = tmpname; - map = 0; - } - } - if (map) { + { if (inode->i_sb->u.isofs_sb.s_mapping == 'n') { len = isofs_name_translate(de->name, de->name_len[0], tmpname); diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 88cf1fd4a..9445b921e 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -283,7 +283,7 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, struct iso_supplementary_descriptor *sec = NULL; struct iso_volume_descriptor * vdp; unsigned int vol_desc_start; - + struct inode * inode; MOD_INC_USE_COUNT; @@ -384,7 +384,6 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, sec = (struct iso_supplementary_descriptor *)vdp; if (sec->escape[0] == 0x25 && sec->escape[1] == 0x2f) { if (opt.joliet == 'y') { - opt.rock = 'n'; if (sec->escape[2] == 0x40) { joliet_level = 1; } else if (sec->escape[2] == 0x43) { @@ -418,16 +417,12 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, s->u.isofs_sb.s_joliet_level = joliet_level; -#ifdef CONFIG_JOLIET - if (joliet_level) { - /* Note: In theory, it is possible to have Rock Ridge - * extensions mixed with Joliet. All character strings - * would just be saved as Unicode. Until someone sees such - * a disc, do not allow the two to be mixed + if (joliet_level && opt.rock == 'n') { + /* This is the case of Joliet with the norock mount flag. + * A disc with both Joliet and Rock Ridge is handled later */ pri = (struct iso_primary_descriptor *) sec; } -#endif if(high_sierra){ rootp = (struct iso_directory_record *) h_pri->root_directory_record; @@ -486,11 +481,6 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, goto out; } - /* RDE: data zone now byte offset! */ - - s->u.isofs_sb.s_firstdatazone = ((isonum_733 (rootp->extent) + - isonum_711 (rootp->ext_attr_length)) - << s -> u.isofs_sb.s_log_zone_size); s->s_magic = ISOFS_SUPER_MAGIC; /* The CDROM is read-only, has no nodes (devices) on it, and since @@ -502,14 +492,18 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, brelse(bh); + /* RDE: data zone now byte offset! */ + + s->u.isofs_sb.s_firstdatazone = ((isonum_733 (rootp->extent) + + isonum_711 (rootp->ext_attr_length)) + << s -> u.isofs_sb.s_log_zone_size); #ifndef BEQUIET printk(KERN_DEBUG "Max size:%ld Log zone size:%ld\n", s->u.isofs_sb.s_max_size, 1UL << s->u.isofs_sb.s_log_zone_size); printk(KERN_DEBUG "First datazone:%ld Root inode number %d\n", s->u.isofs_sb.s_firstdatazone >> s -> u.isofs_sb.s_log_zone_size, - (isonum_733(rootp->extent) + isonum_711(rootp->ext_attr_length)) - << s -> u.isofs_sb.s_log_zone_size); + s->u.isofs_sb.s_firstdatazone); if(high_sierra) printk(KERN_DEBUG "Disc in High Sierra format.\n"); #endif unlock_super(s); @@ -549,8 +543,9 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, #endif } -#ifdef CONFIG_JOLIET s->u.isofs_sb.s_nls_iocharset = NULL; + +#ifdef CONFIG_JOLIET if (joliet_level == 0) { if (opt.iocharset) { kfree(opt.iocharset); @@ -574,7 +569,7 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, s->s_dev = dev; s->s_op = &isofs_sops; s->u.isofs_sb.s_mapping = opt.map; - s->u.isofs_sb.s_rock = (opt.rock == 'y' ? 1 : 0); + s->u.isofs_sb.s_rock = (opt.rock == 'y' ? 2 : 0); s->u.isofs_sb.s_name_check = opt.check; s->u.isofs_sb.s_conversion = opt.conversion; s->u.isofs_sb.s_cruft = opt.cruft; @@ -589,9 +584,30 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, s->u.isofs_sb.s_mode = opt.mode & 0777; s->s_blocksize = opt.blocksize; s->s_blocksize_bits = blocksize_bits; - s->s_root = d_alloc_root(iget(s, (isonum_733(rootp->extent) + - isonum_711(rootp->ext_attr_length)) - << s -> u.isofs_sb.s_log_zone_size), NULL); + inode = iget(s, s->u.isofs_sb.s_firstdatazone); + + /* + * If this disk has both Rock Ridge and Joliet on it, then we + * want to use Rock Ridge by default. This can be overridden + * by using the norock mount option. There is still one other + * possibility that is not taken into account: a Rock Ridge + * CD with Unicode names. Until someone sees such a beast, it + * will not be supported. + */ + if (joliet_level && opt.rock == 'y' && s->u.isofs_sb.s_rock != 1) { + iput(inode); + pri = (struct iso_primary_descriptor *) sec; + rootp = (struct iso_directory_record *) + pri->root_directory_record; + s->u.isofs_sb.s_firstdatazone = + ((isonum_733 (rootp->extent) + + isonum_711 (rootp->ext_attr_length)) + << s -> u.isofs_sb.s_log_zone_size); + inode = iget(s, s->u.isofs_sb.s_firstdatazone); + s->u.isofs_sb.s_rock = 0; + } + + s->s_root = d_alloc_root(inode, NULL); unlock_super(s); if (!(s->s_root)) { diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index e006ac3ee..7d943d0fe 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -265,6 +265,7 @@ int parse_rock_ridge_inode(struct iso_directory_record * de, CHECK_CE; break; case SIG('E','R'): + inode->i_sb->u.isofs_sb.s_rock = 1; printk(KERN_DEBUG"ISO9660 Extensions: "); { int p; for(p=0;pu.ER.len_id;p++) printk("%c",rr->u.ER.data[p]); diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index a0eb8165a..38f131d84 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -173,56 +173,60 @@ static int msdos_find(struct inode *dir,const char *name,int len, return fat_scan(dir,msdos_name,bh,de,ino,scantype); } - +/* + * Compute the hash for the msdos name corresponding to the dentry. + * Note: if the name is invalid, we leave the hash code unchanged so + * that the existing dentry can be used. The msdos fs routines will + * return ENOENT or EINVAL as appropriate. + */ static int msdos_hash(struct dentry *dentry, struct qstr *qstr) { - unsigned long hash; - char msdos_name[MSDOS_NAME]; + struct fat_mount_options *options = & (MSDOS_SB(dentry->d_sb)->options); int error; - int i; - struct fat_mount_options *options = - & (MSDOS_SB(dentry->d_inode->i_sb)->options); + char msdos_name[MSDOS_NAME]; - error = msdos_format_name(options->name_check, - qstr->name, qstr->len, msdos_name,1, - options->dotsOK); - if(error) - return error; - hash = init_name_hash(); - for(i=0; i< MSDOS_NAME; i++) - hash = partial_name_hash(msdos_name[i], hash); - qstr->hash = end_name_hash(hash); + error = msdos_format_name(options->name_check, qstr->name, qstr->len, + msdos_name, 1, options->dotsOK); + if (!error) + qstr->hash = full_name_hash(msdos_name, MSDOS_NAME); return 0; } - -static int msdos_cmp(struct dentry *dentry, - struct qstr *a, struct qstr *b) +/* + * Compare two msdos names. If either of the names are invalid, + * we fall back to doing the standard name comparison. + */ +static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b) { - char a_msdos_name[MSDOS_NAME],b_msdos_name[MSDOS_NAME]; + struct fat_mount_options *options = & (MSDOS_SB(dentry->d_sb)->options); int error; - struct fat_mount_options *options = - & (MSDOS_SB(dentry->d_inode->i_sb)->options); - - error = msdos_format_name(options->name_check, - a->name, a->len, a_msdos_name,1, - options->dotsOK); - if(error) - return error; - error = msdos_format_name(options->name_check, - b->name, b->len, b_msdos_name,1, - options->dotsOK); - if(error) - return error; - - return memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME); + char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME]; + + error = msdos_format_name(options->name_check, a->name, a->len, + a_msdos_name, 1, options->dotsOK); + if (error) + goto old_compare; + error = msdos_format_name(options->name_check, b->name, b->len, + b_msdos_name, 1, options->dotsOK); + if (error) + goto old_compare; + error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME); +out: + return error; + +old_compare: + error = 1; + if (a->len == b->len) + error = memcmp(a->name, b->name, a->len); + goto out; } static struct dentry_operations msdos_dentry_operations = { - 0, /* d_revalidate */ + NULL, /* d_revalidate */ msdos_hash, - msdos_cmp + msdos_cmp, + NULL /* d_delete */ }; struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent) @@ -233,14 +237,16 @@ struct super_block *msdos_read_super(struct super_block *sb,void *data, int sile MSDOS_SB(sb)->options.isvfat = 0; sb->s_op = &msdos_sops; - res = fat_read_super(sb, data, silent); - if (res == NULL) { - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return NULL; - } + res = fat_read_super(sb, data, silent); + if (res == NULL) + goto out_fail; sb->s_root->d_op = &msdos_dentry_operations; return res; + +out_fail: + sb->s_dev = 0; + MOD_DEC_USE_COUNT; + return NULL; } @@ -290,11 +296,14 @@ int msdos_lookup(struct inode *dir,struct dentry *dentry) PRINTK (("msdos_lookup 6\n")); while (MSDOS_I(inode)->i_old) { next = MSDOS_I(inode)->i_old; +#ifdef MSDOS_PARANOIA +printk("msdos_lookup: ino %ld, old ino=%ld\n", inode->i_ino, next->i_ino); +if (MSDOS_I(next)->i_depend != inode) +printk("msdos_lookup: depend=%p, inode=%p??\n", MSDOS_I(next)->i_depend, inode); +#endif + next->i_count++; iput(inode); - if (!(inode = iget(next->i_sb,next->i_ino))) { - fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen"); - return -ENOENT; /* N.B. Maybe ENOMEM is better? */ - } + inode = next; } PRINTK (("msdos_lookup 7\n")); d_add(dentry, inode); diff --git a/fs/namei.c b/fs/namei.c index 2e7c4bff6..ab8d6089e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -73,6 +73,10 @@ * and in the old Linux semantics. */ +/* [16-Dec-97 Kevin Buhr] For security reasons, we change some symlink + * semantics. See the comments in "open_namei" and "do_link" below. + */ + static char * quicklist = NULL; static int quickcount = 0; struct semaphore quicklock = MUTEX; @@ -530,7 +534,13 @@ struct dentry * open_namei(const char * pathname, int flag, int mode) mode &= S_IALLUGO & ~current->fs->umask; mode |= S_IFREG; - dentry = lookup_dentry(pathname, NULL, 1); + /* + * Special case: O_CREAT|O_EXCL on a dangling symlink should + * give EEXIST for security reasons. While inconsistent, this + * is the same scheme used by, for example, Solaris 2.5.1. --KAB + */ + dentry = lookup_dentry(pathname, NULL, + (flag & (O_CREAT|O_EXCL)) != (O_CREAT|O_EXCL)); if (IS_ERR(dentry)) return dentry; @@ -731,7 +741,7 @@ static inline int do_mkdir(const char * pathname, int mode) struct dentry *dir; struct dentry *dentry; - dentry = lookup_dentry(pathname, NULL, 1); + dentry = lookup_dentry(pathname, NULL, 0); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto exit; @@ -1010,7 +1020,16 @@ static inline int do_link(const char * oldname, const char * newname) if (IS_ERR(old_dentry)) goto exit; - new_dentry = lookup_dentry(newname, NULL, 1); + /* + * Hardlinks are often used in delicate situations. We avoid + * security-related surprises by not following symlinks on the + * newname. We *do* follow them on the oldname. This is + * the same as Digital Unix 4.0, for example. + * + * Solaris 2.5.1 is similar, but for a laugh try linking from + * a dangling symlink. --KAB + */ + new_dentry = lookup_dentry(newname, NULL, 0); error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) goto exit_old; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index c74c73243..aaf17187b 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -471,11 +471,11 @@ void nfs_renew_times(struct dentry * dentry) static int nfs_lookup(struct inode *dir, struct dentry * dentry) { + int len = dentry->d_name.len; struct inode *inode; + int error; struct nfs_fh fhandle; struct nfs_fattr fattr; - int len = dentry->d_name.len; - int error; dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n", dir->i_dev, dir->i_ino, len, dentry->d_name.name); @@ -515,11 +515,39 @@ out: return error; } +/* + * Attempt to patch up certain errors following a create or + * mkdir operation. We clear the original error if the new + * lookup succeeds and has the correct mode. + */ +static int nfs_fixup(struct inode *dir, struct dentry *dentry, int mode, + struct nfs_fh *fhandle, struct nfs_fattr *fattr, int error) +{ + int newerr; + +#ifdef NFS_PARANOIA +printk("nfs_fixup: %s/%s, error=%d, mode=%x\n", +dentry->d_parent->d_name.name, dentry->d_name.name, error, mode); +#endif + if (error == -EEXIST) { + newerr = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), + dentry->d_name.name, fhandle, fattr); + if (!newerr) { +#ifdef NFS_PARANOIA +printk("nfs_fixup: lookup OK, got mode=%x, want mode=%x\n", fattr->mode, mode); +#endif + if ((fattr->mode & S_IFMT) == (mode & S_IFMT)) + error = 0; + } + } + return error; +} + /* * Code common to create, mkdir, and mknod. */ -static int nfs_instantiate(struct dentry *dentry, struct nfs_fattr *fattr, - struct nfs_fh *fhandle) +static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, + struct nfs_fattr *fattr) { struct inode *inode; int error = -EACCES; @@ -545,12 +573,12 @@ inode->i_ino, inode->i_count, inode->i_nlink); * that the operation succeeded on the server, but an error in the * reply path made it appear to have failed. */ -static int nfs_create(struct inode *dir, struct dentry * dentry, int mode) +static int nfs_create(struct inode *dir, struct dentry *dentry, int mode) { + int error; struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; - int error; dfprintk(VFS, "NFS: create(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); @@ -574,9 +602,11 @@ static int nfs_create(struct inode *dir, struct dentry * dentry, int mode) nfs_invalidate_dircache(dir); error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, &sattr, &fhandle, &fattr); + if (error) + error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error); if (!error) - error = nfs_instantiate(dentry, &fattr, &fhandle); - else + error = nfs_instantiate(dentry, &fhandle, &fattr); + if (error) d_drop(dentry); out: return error; @@ -587,10 +617,10 @@ out: */ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) { + int error; struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; - int error; dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); @@ -612,9 +642,11 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde nfs_invalidate_dircache(dir); error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, &sattr, &fhandle, &fattr); + if (error) + error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error); if (!error) - error = nfs_instantiate(dentry, &fattr, &fhandle); - else + error = nfs_instantiate(dentry, &fhandle, &fattr); + if (error) d_drop(dentry); return error; } @@ -624,10 +656,10 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde */ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { + int error; struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; - int error; dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); @@ -640,6 +672,8 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; + /* For some reason mode doesn't have the S_IFDIR flag ... */ + mode |= S_IFDIR; sattr.mode = mode; sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; @@ -647,6 +681,8 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) nfs_invalidate_dircache(dir); error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, &sattr, &fhandle, &fattr); + if (error) + error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error); if (!error) { /* * Some AIX servers reportedly fail to fill out the fattr. @@ -660,8 +696,9 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) fattr.mode); goto drop; } - error = nfs_instantiate(dentry, &fattr, &fhandle); - } else { + error = nfs_instantiate(dentry, &fhandle, &fattr); + } + if (error) { drop: d_drop(dentry); } @@ -858,11 +895,8 @@ out: * Remove a file after making sure there are no pending writes, * and after checking that the file has only one user. * - * Updating inode->i_nlink here rather than waiting for the next - * nfs_refresh_inode() is not merely cosmetic; once an object has - * been deleted, we want to get rid of the inode locally. The NFS - * server may reuse the fileid for a new inode, and we don't want - * that to be confused with this inode. + * We update inode->i_nlink and free the inode prior to the operation + * to avoid possible races if the server reuses the inode. */ static int nfs_safe_remove(struct dentry *dentry) { @@ -870,6 +904,7 @@ static int nfs_safe_remove(struct dentry *dentry) struct inode *inode = dentry->d_inode; int error, rehash = 0; + /* N.B. not needed now that d_delete is done in advance? */ error = -EBUSY; if (inode) { if (NFS_WRITEBACK(inode)) { @@ -897,7 +932,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); goto out; } #ifdef NFS_PARANOIA -if (inode && inode->i_count > 1) +if (inode && inode->i_count > inode->i_nlink) printk("nfs_safe_remove: %s/%s inode busy?? i_count=%d, i_nlink=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count, inode->i_nlink); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index c070d130b..eb56950eb 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -35,9 +35,6 @@ #define NFSDBG_FACILITY NFSDBG_VFS #define NFS_PARANOIA 1 -extern void nfs_invalidate_dircache_sb(struct super_block *); -extern int check_failed_request(struct inode *); - static void nfs_read_inode(struct inode *); static void nfs_put_inode(struct inode *); static void nfs_delete_inode(struct inode *); @@ -99,8 +96,9 @@ nfs_delete_inode(struct inode * inode) */ if (NFS_WRITEBACK(inode) != NULL) { unsigned long timeout = jiffies + 5*HZ; - printk("NFS: inode %ld, invalidating pending RPC requests\n", - inode->i_ino); +#ifdef NFS_DEBUG_VERBOSE +printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino); +#endif nfs_invalidate_pages(inode); while (NFS_WRITEBACK(inode) != NULL && jiffies < timeout) { current->state = TASK_INTERRUPTIBLE; @@ -112,7 +110,7 @@ nfs_delete_inode(struct inode * inode) printk("NFS: Arghhh, stuck RPC requests!\n"); } - failed = check_failed_request(inode); + failed = nfs_check_failed_request(inode); if (failed) printk("NFS: inode %ld had %d failed requests\n", inode->i_ino, failed); @@ -355,8 +353,6 @@ nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) * instead of inode number. We use this technique instead of using * the vfs read_inode function because there is no way to pass the * file handle or current attributes into the read_inode function. - * We just have to be careful not to subvert iget's special handling - * of mount points. */ struct inode * nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle, @@ -422,8 +418,10 @@ printk("nfs_fhget: impossible\n"); inode->i_size = fattr->size; inode->i_mtime = fattr->mtime.seconds; NFS_OLDMTIME(inode) = fattr->mtime.seconds; + *NFS_FH(inode) = *fhandle; } - *NFS_FH(inode) = *fhandle; + if (memcmp(NFS_FH(inode), fhandle, sizeof(struct nfs_fh))) + printk("nfs_fhget: fhandle changed!\n"); nfs_refresh_inode(inode, fattr); dprintk("NFS: fhget(%x/%ld ct=%d)\n", inode->i_dev, inode->i_ino, diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 97663cc11..53c227e58 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -68,8 +68,6 @@ #define NFSDBG_FACILITY NFSDBG_PAGECACHE -int check_failed_request(struct inode *); - static void nfs_wback_lock(struct rpc_task *task); static void nfs_wback_result(struct rpc_task *task); @@ -331,7 +329,7 @@ remove_failed_request(struct nfs_wreq * req) * Find and release all failed requests for this inode. */ int -check_failed_request(struct inode * inode) +nfs_check_failed_request(struct inode * inode) { struct nfs_wreq * req; int found = 0; @@ -496,8 +494,7 @@ wait_on_write_request(struct nfs_wreq *req) } remove_wait_queue(&page->wait, &wait); current->state = TASK_RUNNING; - if (atomic_read(&page->count) == 1) - printk("NFS: page unused while waiting\n"); + /* N.B. page may have been unused, so we must use free_page() */ free_page(page_address(page)); return retval; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 81b9a3f79..29a36a554 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -62,7 +62,8 @@ extern void fh_update(struct svc_fh*); struct raparms { struct raparms *p_next; unsigned int p_count; - struct dentry *p_dentry; + ino_t p_ino; + dev_t p_dev; unsigned long p_reada, p_ramax, p_raend, @@ -316,29 +317,27 @@ nfsd_sync(struct inode *inode, struct file *filp) } /* - * Obtain the readahead parameters for the given file - * - * N.B. is raparm cache for a file cleared when the file closes?? - * (dentry might be reused later.) + * Obtain the readahead parameters for the file + * specified by (dev, ino). */ static inline struct raparms * -nfsd_get_raparms(struct dentry *dentry) +nfsd_get_raparms(dev_t dev, ino_t ino) { struct raparms *ra, **rap, **frap = NULL; for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) { - if (ra->p_dentry != dentry) { - if (ra->p_count == 0) - frap = rap; - } else + if (ra->p_ino == ino && ra->p_dev == dev) goto found; + if (ra->p_count == 0) + frap = rap; } if (!frap) return NULL; rap = frap; ra = *frap; memset(ra, 0, sizeof(*ra)); - ra->p_dentry = dentry; + ra->p_dev = dev; + ra->p_ino = ino; found: if (rap != &raparm_cache) { *rap = ra->p_next; @@ -359,23 +358,20 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf, unsigned long *count) { struct raparms *ra; - struct dentry *dentry; - struct inode *inode; - struct file file; mm_segment_t oldfs; int err; + struct file file; - if ((err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_READ, &file)) != 0) - return err; - dentry = file.f_dentry; - inode = dentry->d_inode; - if (!file.f_op->read) { - nfsd_close(&file); - return nfserr_perm; - } + err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_READ, &file); + if (err) + goto out; + err = nfserr_perm; + if (!file.f_op->read) + goto out_close; /* Get readahead parameters */ - if ((ra = nfsd_get_raparms(dentry)) != NULL) { + ra = nfsd_get_raparms(fhp->fh_handle.fh_dev, fhp->fh_handle.fh_ino); + if (ra) { file.f_reada = ra->p_reada; file.f_ramax = ra->p_ramax; file.f_raend = ra->p_raend; @@ -401,12 +397,15 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf, ra->p_count -= 1; } + if (err >= 0) { + *count = err; + err = 0; + } else + err = nfserrno(-err); +out_close: nfsd_close(&file); - - if (err < 0) - return nfserrno(-err); - *count = err; - return 0; +out: + return err; } /* @@ -423,16 +422,16 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, struct dentry *dentry; struct inode *inode; mm_segment_t oldfs; - int err; + int err = 0; if (!cnt) - return 0; - if ((err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_WRITE, &file)) != 0) - return err; - if (!file.f_op->write) { - nfsd_close(&file); - return nfserr_perm; - } + goto out; + err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_WRITE, &file); + if (err) + goto out; + err = nfserr_perm; + if (!file.f_op->write) + goto out_close; dentry = file.f_dentry; inode = dentry->d_inode; @@ -507,10 +506,15 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, last_dev = inode->i_dev; } - nfsd_close(&file); - dprintk("nfsd: write complete\n"); - return (err < 0)? nfserrno(-err) : 0; + if (err >= 0) + err = 0; + else + err = nfserrno(-err); +out_close: + nfsd_close(&file); +out: + return err; } /* @@ -824,20 +828,27 @@ out: return err; } -/* More "hidden treasure" from the generic VFS. -DaveM */ -/* N.B. VFS double_down was modified to fix a bug ... should use VFS one */ +/* + * This follows the model of double_lock() in the VFS. + */ static inline void nfsd_double_down(struct semaphore *s1, struct semaphore *s2) { - if((unsigned long) s1 < (unsigned long) s2) { - down(s1); - down(s2); - } else if(s1 == s2) { - down(s1); - atomic_dec(&s1->count); - } else { - down(s2); + if (s1 != s2) { + if ((unsigned long) s1 < (unsigned long) s2) { + struct semaphore *tmp = s1; + s1 = s2; + s2 = tmp; + } down(s1); } + down(s2); +} + +static inline void nfsd_double_up(struct semaphore *s1, struct semaphore *s2) +{ + up(s1); + if (s1 != s2) + up(s2); } /* @@ -866,43 +877,47 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, tdir = tdentry->d_inode; /* N.B. We shouldn't need this ... dentry layer handles it */ + err = nfserr_perm; if (!flen || (fname[0] == '.' && (flen == 1 || (flen == 2 && fname[1] == '.'))) || !tlen || (tname[0] == '.' && (tlen == 1 || (tlen == 2 && tname[1] == '.')))) - return nfserr_perm; + goto out; + + err = -EXDEV; + if (fdir->i_dev != tdir->i_dev) + goto out_nfserr; + err = -EPERM; + if (!fdir->i_op || !fdir->i_op->rename) + goto out_nfserr; odentry = lookup_dentry(fname, dget(fdentry), 0); err = PTR_ERR(odentry); if (IS_ERR(odentry)) - goto out_no_unlock; + goto out_nfserr; ndentry = lookup_dentry(tname, dget(tdentry), 0); err = PTR_ERR(ndentry); if (IS_ERR(ndentry)) goto out_dput_old; - /* N.B. check this ... problems in locking?? */ + /* + * Lock the parent directories. + */ nfsd_double_down(&tdir->i_sem, &fdir->i_sem); - err = -EXDEV; - if (fdir->i_dev != tdir->i_dev) - goto out_unlock; - err = -EPERM; - if (!fdir->i_op || !fdir->i_op->rename) - goto out_unlock; + /* N.B. check for parent changes after locking?? */ + err = fdir->i_op->rename(fdir, odentry, tdir, ndentry); if (!err && EX_ISSYNC(tfhp->fh_export)) { write_inode_now(fdir); write_inode_now(tdir); } -out_unlock: - up(&tdir->i_sem); - up(&fdir->i_sem); - + nfsd_double_up(&tdir->i_sem, &fdir->i_sem); dput(ndentry); + out_dput_old: dput(odentry); -out_no_unlock: +out_nfserr: if (err) err = nfserrno(-err); out: diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 89da7544b..a467af4df 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -154,7 +154,7 @@ proc_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) break; } - n -= copy_to_user(buf, start, n); + n -= copy_to_user(buf, start, n); /* BUG ??? */ if (n == 0) { if (retval == 0) retval = -EFAULT; diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index 921aa86e1..5af80e91b 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -85,7 +85,18 @@ find_inode_number(struct dentry *dir, struct qstr *name) struct dentry * dentry; ino_t ino = 0; + /* + * Check for a fs-specific hash function. Note that we must + * calculate the standard hash first, as the d_op->d_hash() + * routine may choose to leave the hash value unchanged. + */ name->hash = full_name_hash(name->name, name->len); + if (dir->d_op && dir->d_op->d_hash) + { + if (dir->d_op->d_hash(dir, name) != 0) + goto out; + } + dentry = d_lookup(dir, name); if (dentry) { @@ -93,6 +104,7 @@ find_inode_number(struct dentry *dir, struct qstr *name) ino = dentry->d_inode->i_ino; dput(dentry); } +out: return ino; } @@ -179,27 +191,51 @@ out: return result; } +/* + * Note: in order to allow the smbclient process to open the + * mount point, we don't revalidate for the connection pid. + */ static int smb_dir_open(struct inode *dir, struct file *file) { + struct dentry *dentry = file->f_dentry; + struct smb_sb_info *server = server_from_dentry(dentry); + int error = 0; #ifdef SMBFS_DEBUG_VERBOSE -printk("smb_dir_open: (%s/%s)\n", file->f_dentry->d_parent->d_name.name, +printk("smb_dir_open: (%s/%s)\n", dentry->d_parent->d_name.name, file->f_dentry->d_name.name); #endif - return smb_revalidate_inode(dir); + /* + * Directory timestamps in the core protocol aren't updated + * when a file is added, so we give them a very short TTL. + */ + if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) + { + unsigned long age = jiffies - dir->u.smbfs_i.oldmtime; + if (age > 2*HZ) + smb_invalid_dir_cache(dir); + } + + if (server->conn_pid) + error = smb_revalidate_inode(dir); + else + printk("smb_dir_open: smbclient process\n"); + return error; } /* * Dentry operations routines */ static int smb_lookup_validate(struct dentry *); +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 *) */ - NULL, /* d_hash */ - NULL, /* d_compare */ + smb_hash_dentry, /* d_hash */ + smb_compare_dentry, /* d_compare */ smb_delete_dentry /* d_delete(struct dentry *) */ }; @@ -218,7 +254,7 @@ smb_lookup_validate(struct dentry * dentry) * we believe in dentries for 5 seconds. (But each * successful server lookup renews the timestamp.) */ - valid = (age <= SMBFS_MAX_AGE) || IS_ROOT(dentry); + valid = (age <= SMBFS_MAX_AGE); #ifdef SMBFS_DEBUG_VERBOSE if (!valid) printk("smb_lookup_validate: %s/%s not valid, age=%lu\n", @@ -234,7 +270,8 @@ printk("smb_lookup_validate: %s/%s has dud inode\n", dentry->d_parent->d_name.name, dentry->d_name.name); #endif valid = 0; - } + } else if (!valid) + valid = (smb_revalidate_inode(inode) == 0); } else { /* @@ -244,6 +281,43 @@ dentry->d_parent->d_name.name, dentry->d_name.name); return valid; } +/* + * XXX: It would be better to use the tolower from linux/ctype.h, + * but _ctype is needed and it is not exported. + */ +#define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)-('A'-'a') : (c)) + +static int +smb_hash_dentry(struct dentry *dir, struct qstr *this) +{ + unsigned long hash; + int i; + + hash = init_name_hash(); + for (i=0; i < this->len ; i++) + hash = partial_name_hash(tolower(this->name[i]), hash); + this->hash = end_name_hash(hash); + + return 0; +} + +static int +smb_compare_dentry(struct dentry *dir, struct qstr *a, struct qstr *b) +{ + int i, result = 1; + + if (a->len != b->len) + goto out; + for (i=0; i < a->len; i++) + { + if (tolower(a->name[i]) != tolower(b->name[i])) + goto out; + } + result = 0; +out: + return result; +} + /* * This is the callback from dput() when d_count is going to 0. * We use this to unhash dentries with bad inodes and close files. @@ -251,15 +325,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name); static void smb_delete_dentry(struct dentry * dentry) { - if ((jiffies - dentry->d_time) > SMBFS_MAX_AGE) - { -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_delete_dentry: %s/%s expired, d_time=%lu, now=%lu\n", -dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_time, jiffies); -#endif - d_drop(dentry); - } - if (dentry->d_inode) { if (is_bad_inode(dentry->d_inode)) @@ -305,7 +370,7 @@ smb_lookup(struct inode *dir, struct dentry *dentry) if (dentry->d_name.len > SMB_MAXNAMELEN) goto out; - error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &finfo); + error = smb_proc_getattr(dentry, &finfo); #ifdef SMBFS_PARANOIA if (error && error != -ENOENT) printk("smb_lookup: find %s/%s failed, error=%d\n", @@ -350,7 +415,7 @@ smb_instantiate(struct dentry *dentry, __u16 fileid, int have_id) printk("smb_instantiate: file %s/%s, fileid=%u\n", dentry->d_parent->d_name.name, dentry->d_name.name, fileid); #endif - error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr); + error = smb_proc_getattr(dentry, &fattr); if (error) goto out_close; @@ -402,9 +467,9 @@ dentry->d_parent->d_name.name, dentry->d_name.name, mode); goto out; smb_invalid_dir_cache(dir); - error = smb_proc_create(dentry->d_parent, &(dentry->d_name), - 0, CURRENT_TIME, &fileid); - if (!error) { + error = smb_proc_create(dentry, 0, CURRENT_TIME, &fileid); + if (!error) + { error = smb_instantiate(dentry, fileid, 1); } else { @@ -428,7 +493,7 @@ smb_mkdir(struct inode *dir, struct dentry *dentry, int mode) goto out; smb_invalid_dir_cache(dir); - error = smb_proc_mkdir(dentry->d_parent, &(dentry->d_name)); + error = smb_proc_mkdir(dentry); if (!error) { error = smb_instantiate(dentry, 0, 0); @@ -440,17 +505,17 @@ out: static int smb_rmdir(struct inode *dir, struct dentry *dentry) { + struct inode *inode = dentry->d_inode; int error; - error = -ENAMETOOLONG; - if (dentry->d_name.len > SMB_MAXNAMELEN) + error = -ENOTDIR; + if (!S_ISDIR(inode->i_mode)) goto out; /* - * Since the dentry is holding an inode, the file - * is in use, so we have to close it first. + * Close the directory if it's open. */ - smb_close(dentry->d_inode); + smb_close(inode); /* * Prune any child dentries so this dentry can become negative. @@ -463,7 +528,7 @@ smb_rmdir(struct inode *dir, struct dentry *dentry) } smb_invalid_dir_cache(dir); - error = smb_proc_rmdir(dentry->d_parent, &(dentry->d_name)); + error = smb_proc_rmdir(dentry); if (!error) { smb_renew_times(dentry); @@ -478,24 +543,18 @@ smb_unlink(struct inode *dir, struct dentry *dentry) { int error; - error = -ENAMETOOLONG; - if (dentry->d_name.len > SMB_MAXNAMELEN) - goto out; - /* - * Since the dentry is holding an inode, the file - * is in use, so we have to close it first. + * Close the file if it's open. */ smb_close(dentry->d_inode); smb_invalid_dir_cache(dir); - error = smb_proc_unlink(dentry->d_parent, &(dentry->d_name)); + error = smb_proc_unlink(dentry); if (!error) { smb_renew_times(dentry); d_delete(dentry); } -out: return error; } @@ -511,50 +570,33 @@ smb_rename(struct inode *old_dir, struct dentry *old_dentry, goto out; /* - * Since the old and new dentries are holding the files open, - * we have to close the files first. + * Close any open files, and check whether to delete the + * target before attempting the rename. */ if (old_dentry->d_inode) smb_close(old_dentry->d_inode); if (new_dentry->d_inode) - smb_close(new_dentry->d_inode); - - smb_invalid_dir_cache(old_dir); - smb_invalid_dir_cache(new_dir); - error = smb_proc_mv(old_dentry->d_parent, &(old_dentry->d_name), - new_dentry->d_parent, &(new_dentry->d_name)); - /* - * If the new file exists, attempt to delete it. - */ - if (error == -EEXIST) { + smb_close(new_dentry->d_inode); + error = smb_proc_unlink(new_dentry); + if (error) + { #ifdef SMBFS_DEBUG_VERBOSE -printk("smb_rename: existing file %s/%s, d_count=%d\n", -new_dentry->d_parent->d_name.name, new_dentry->d_name.name, -new_dentry->d_count); -#endif - error = smb_proc_unlink(new_dentry->d_parent, - &(new_dentry->d_name)); -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_rename: after unlink error=%d\n", error); +printk("smb_rename: unlink %s/%s, error=%d\n", +new_dentry->d_parent->d_name.name, new_dentry->d_name.name, error); #endif - if (!error) - { - d_delete(new_dentry); - error = smb_proc_mv(old_dentry->d_parent, - &(old_dentry->d_name), - new_dentry->d_parent, - &(new_dentry->d_name)); + goto out; } + d_delete(new_dentry); } - /* - * Update the dcache - */ + smb_invalid_dir_cache(old_dir); + smb_invalid_dir_cache(new_dir); + error = smb_proc_mv(old_dentry, new_dentry); if (!error) { smb_renew_times(old_dentry); - smb_renew_times(new_dentry->d_parent); + smb_renew_times(new_dentry); d_move(old_dentry, new_dentry); } out: diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index 2454fdf8e..c04e0acc0 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -306,9 +306,9 @@ smb_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) ssize_t result; #ifdef SMBFS_DEBUG_VERBOSE -printk("smb_file_write: file %s/%s, count=%lu@%lu\n", +printk("smb_file_write: file %s/%s, count=%lu@%lu, pages=%ld\n", dentry->d_parent->d_name.name, dentry->d_name.name, -(unsigned long) count, (unsigned long) *ppos); +(unsigned long) count, (unsigned long) *ppos, inode->i_nrpages); #endif result = smb_revalidate_inode(inode); diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index b21892863..1e72f59a1 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -55,12 +55,12 @@ static struct super_operations smb_sops = unsigned long smb_invent_inos(unsigned long n) { - static unsigned long ino = 1; + static unsigned long ino = 2; if (ino + 2*n < ino) { /* wrap around */ - ino += n; + ino = 2; } ino += n; return ino; @@ -93,6 +93,7 @@ smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr) memset(fattr, 0, sizeof(struct smb_fattr)); fattr->f_mode = inode->i_mode; fattr->f_nlink = inode->i_nlink; + fattr->f_ino = inode->i_ino; fattr->f_uid = inode->i_uid; fattr->f_gid = inode->i_gid; fattr->f_rdev = inode->i_rdev; @@ -102,6 +103,15 @@ smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr) fattr->f_atime = inode->i_atime; fattr->f_blksize= inode->i_blksize; fattr->f_blocks = inode->i_blocks; + + fattr->attr = inode->u.smbfs_i.attr; + /* + * Keep the attributes in sync with the inode permissions. + */ + if (fattr->f_mode & S_IWUSR) + fattr->attr &= ~aRONLY; + else + fattr->attr |= aRONLY; } static void @@ -112,12 +122,21 @@ smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr) inode->i_uid = fattr->f_uid; inode->i_gid = fattr->f_gid; inode->i_rdev = fattr->f_rdev; - inode->i_size = fattr->f_size; - inode->i_mtime = fattr->f_mtime; inode->i_ctime = fattr->f_ctime; - inode->i_atime = fattr->f_atime; inode->i_blksize= fattr->f_blksize; inode->i_blocks = fattr->f_blocks; + /* + * Don't change the size and mtime/atime fields + * if we're writing to the file. + */ + if (!(inode->u.smbfs_i.cache_valid & SMB_F_LOCALWRITE)) + { + inode->i_size = fattr->f_size; + inode->i_mtime = fattr->f_mtime; + inode->i_atime = fattr->f_atime; + } + + inode->u.smbfs_i.attr = fattr->attr; /* * Update the "last time refreshed" field for revalidation. */ @@ -177,9 +196,9 @@ smb_revalidate_inode(struct inode *inode) * If this is a file opened with write permissions, * the inode will be up-to-date. */ - if (S_ISREG(inode->i_mode) && smb_is_open(inode)) { - if (inode->u.smbfs_i.access == SMB_O_RDWR || - inode->u.smbfs_i.access == SMB_O_WRONLY) + if (S_ISREG(inode->i_mode) && smb_is_open(inode)) + { + if (inode->u.smbfs_i.access != SMB_O_RDONLY) goto out; } @@ -237,15 +256,7 @@ smb_refresh_inode(struct inode *inode) goto out; } - /* - * Kludge alert ... for some reason we can't get attributes - * for the root directory, so just return success. - */ - error = 0; - if (IS_ROOT(dentry)) - goto out; - - error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr); + error = smb_proc_getattr(dentry, &fattr); if (!error) { smb_renew_times(dentry); @@ -261,12 +272,8 @@ smb_refresh_inode(struct inode *inode) * Big trouble! The inode has become a new object, * so any operations attempted on it are invalid. * - * Take a couple of steps to limit damage: - * (1) Mark the inode as bad so that subsequent - * lookup validations will fail. - * (2) Clear i_nlink so the inode will be released - * at iput() time. (Unhash it as well?) - * We also invalidate the caches for good measure. + * To limit damage, mark the inode as bad so that + * subsequent lookup validations will fail. */ #ifdef SMBFS_PARANOIA printk("smb_refresh_inode: %s/%s changed mode, %07o to %07o\n", @@ -354,7 +361,7 @@ smb_put_super(struct super_block *sb) } if (server->conn_pid) - kill_proc(server->conn_pid, SIGTERM, 0); + kill_proc(server->conn_pid, SIGTERM, 1); kfree(server->mnt); if (server->packet) @@ -487,7 +494,8 @@ smb_notify_change(struct inode *inode, struct iattr *attr) struct smb_sb_info *server = SMB_SERVER(inode); struct dentry *dentry = inode->u.smbfs_i.dentry; unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO); - int error, refresh = 0; + int error, changed, refresh = 0; + struct smb_fattr fattr; error = -EIO; if (!dentry) @@ -515,14 +523,14 @@ smb_notify_change(struct inode *inode, struct iattr *attr) if ((attr->ia_valid & ATTR_SIZE) != 0) { - error = smb_open(dentry, O_WRONLY); - if (error) - goto out; #ifdef SMBFS_DEBUG_VERBOSE printk("smb_notify_change: changing %s/%s, old size=%ld, new size=%ld\n", dentry->d_parent->d_name.name, dentry->d_name.name, (long) inode->i_size, (long) attr->ia_size); #endif + error = smb_open(dentry, O_WRONLY); + if (error) + goto out; error = smb_proc_trunc(server, inode->u.smbfs_i.fileid, attr->ia_size); if (error) @@ -531,32 +539,76 @@ dentry->d_parent->d_name.name, dentry->d_name.name, * We don't implement an i_op->truncate operation, * so we have to update the page cache here. */ - if (attr->ia_size < inode->i_size) { + if (attr->ia_size < inode->i_size) + { truncate_inode_pages(inode, attr->ia_size); inode->i_size = attr->ia_size; } refresh = 1; } - if ((attr->ia_valid & (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME)) != 0) - { - struct smb_fattr fattr; - - smb_get_inode_attr(inode, &fattr); - if ((attr->ia_valid & ATTR_CTIME) != 0) - fattr.f_ctime = attr->ia_ctime; - - if ((attr->ia_valid & ATTR_MTIME) != 0) - fattr.f_mtime = attr->ia_mtime; - - if ((attr->ia_valid & ATTR_ATIME) != 0) - fattr.f_atime = attr->ia_atime; + /* + * Initialize the fattr and check for changed fields. + * Note: CTIME under SMB is creation time rather than + * change time, so we don't attempt to change it. + */ + smb_get_inode_attr(inode, &fattr); - error = smb_proc_setattr(server, dentry, &fattr); + changed = 0; + if ((attr->ia_valid & ATTR_MTIME) != 0) + { + fattr.f_mtime = attr->ia_mtime; + changed = 1; + } + if ((attr->ia_valid & ATTR_ATIME) != 0) + { + fattr.f_atime = attr->ia_atime; + /* Earlier protocols don't have an access time */ + if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) + changed = 1; + } + if (changed) + { + error = smb_proc_settime(dentry, &fattr); if (error) goto out; refresh = 1; } + + /* + * Check for mode changes ... we're extremely limited in + * what can be set for SMB servers: just the read-only bit. + */ + if ((attr->ia_valid & ATTR_MODE) != 0) + { +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_notify_change: %s/%s mode change, old=%x, new=%lx\n", +dentry->d_parent->d_name.name, dentry->d_name.name, fattr.f_mode,attr->ia_mode); +#endif + changed = 0; + if (attr->ia_mode & S_IWUSR) + { + if (fattr.attr & aRONLY) + { + fattr.attr &= ~aRONLY; + changed = 1; + } + } else + { + if (!(fattr.attr & aRONLY)) + { + fattr.attr |= aRONLY; + changed = 1; + } + } + if (changed) + { + error = smb_proc_setattr(dentry, &fattr); + if (error) + goto out; + refresh = 1; + } + } error = 0; out: diff --git a/fs/smbfs/ioctl.c b/fs/smbfs/ioctl.c index 5eb3dc88f..f9e6fd4c2 100644 --- a/fs/smbfs/ioctl.c +++ b/fs/smbfs/ioctl.c @@ -11,6 +11,7 @@ #include #include #include + #include #include @@ -20,29 +21,33 @@ int smb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { + struct smb_sb_info *server = SMB_SERVER(inode); int result = -EINVAL; switch (cmd) { case SMB_IOC_GETMOUNTUID: - result = put_user(SMB_SERVER(inode)->mnt->mounted_uid, - (uid_t *) arg); + result = put_user(server->mnt->mounted_uid, (uid_t *) arg); break; case SMB_IOC_NEWCONN: { struct smb_conn_opt opt; - if (arg == 0) + if (arg) { - /* The process offers a new connection upon SIGUSR1 */ - result = smb_offerconn(SMB_SERVER(inode)); + result = -EFAULT; + if (!copy_from_user(&opt, (void *)arg, sizeof(opt))) + result = smb_newconn(server, &opt); } else { - result = -EFAULT; - if (!copy_from_user(&opt, (void *)arg, sizeof(opt))) - result = smb_newconn(SMB_SERVER(inode), &opt); +#if 0 + /* obsolete option ... print a warning */ + printk("SMBFS: ioctl deprecated, please upgrade " + "smbfs package\n"); +#endif + result = 0; } break; } diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index 966ee1e27..05a1ac4f1 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -38,9 +38,8 @@ #define SMB_DIRINFO_SIZE 43 #define SMB_STATUS_SIZE 21 -static int smb_proc_setfile_trans2(struct smb_sb_info *, struct inode *, +static int smb_proc_setattr_ext(struct smb_sb_info *, struct inode *, struct smb_fattr *); - static inline int min(int a, int b) { @@ -237,7 +236,6 @@ date_unix2dos(int unix_date, __u16 *date, __u16 *time) *date = nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9); } - /*****************************************************************************/ /* */ /* Support section. */ @@ -349,7 +347,7 @@ server->packet_size, server->opt.max_xmit, size); return size; } -static int +int smb_errno(struct smb_sb_info *server) { int errcls = server->rcls; @@ -402,7 +400,7 @@ smb_errno(struct smb_sb_info *server) case 123: /* Invalid name?? e.g. .tmp* */ return ENOENT; case 145: /* Win NT 4.0: non-empty directory? */ - return EBUSY; + return ENOTEMPTY; /* This next error seems to occur on an mv when * the destination exists */ case 183: @@ -413,6 +411,7 @@ smb_errno(struct smb_sb_info *server) } else if (errcls == ERRSRV) switch (error) { + /* N.B. This is wrong ... EIO ? */ case ERRerror: return ENFILE; case ERRbadpw: @@ -421,6 +420,13 @@ smb_errno(struct smb_sb_info *server) return EIO; case ERRaccess: return EACCES; + /* + * This is a fatal error, as it means the "tree ID" + * for this connection is no longer valid. We map + * to a special error code and get a new connection. + */ + case ERRinvnid: + return EBADSLT; default: class = "ERRSRV"; goto err_unknown; @@ -474,62 +480,54 @@ smb_unlock_server(struct smb_sb_info *server) of any use. * N.B. The server must be locked for this call. */ - static int smb_retry(struct smb_sb_info *server) { - struct wait_queue wait = { current, NULL }; - unsigned long timeout; - int result = 0; + pid_t pid = server->conn_pid; + int error, result = 0; if (server->state != CONN_INVALID) goto out; smb_close_socket(server); - if (server->conn_pid == 0) + if (pid == 0) { printk("smb_retry: no connection process\n"); server->state = CONN_RETRIED; goto out; } - kill_proc(server->conn_pid, SIGUSR1, 0); -#if 0 + /* + * Clear the pid to enable the ioctl. + */ server->conn_pid = 0; -#endif + /* + * Note: use the "priv" flag, as a user process may need to reconnect. + */ + error = kill_proc(pid, SIGUSR1, 1); + if (error) + { + printk("smb_retry: signal failed, error=%d\n", error); + goto out_restore; + } #ifdef SMBFS_DEBUG_VERBOSE printk("smb_retry: signalled pid %d, waiting for new connection\n", server->conn_pid); #endif /* - * Wait here for a new connection. + * Wait for the new connection. */ - timeout = jiffies + 10*HZ; - add_wait_queue(&server->wait, &wait); - while (1) - { - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + HZ; - if (server->state != CONN_INVALID) - break; - if (jiffies > timeout) - { - printk("smb_retry: timed out, try again later\n"); - break; - } - if (signal_pending(current)) - { - printk("smb_retry: caught signal\n"); - break; - } - schedule(); - } - remove_wait_queue(&server->wait, &wait); + current->timeout = jiffies + 5*HZ; + interruptible_sleep_on(&server->wait); current->timeout = 0; - current->state = TASK_RUNNING; + if (signal_pending(current)) + printk("smb_retry: caught signal\n"); + /* + * Check for a valid connection. + */ if (server->state == CONN_VALID) { #ifdef SMBFS_PARANOIA @@ -538,6 +536,13 @@ printk("smb_retry: new connection pid=%d\n", server->conn_pid); result = 1; } + /* + * Restore the original pid if we didn't get a new one. + */ +out_restore: + if (!server->conn_pid) + server->conn_pid = pid; + out: return result; } @@ -599,43 +604,12 @@ out: } /* - * This is called with the server locked after a successful smb_newconn(). - * It installs the new connection pid, sets server->state to CONN_VALID, - * and wakes up the process waiting for the new connection. - * N.B. The first call is made without locking the server -- need to fix! - */ -int -smb_offerconn(struct smb_sb_info *server) -{ - int error; - - error = -EACCES; - if ((current->uid != server->mnt->mounted_uid) && !suser()) - goto out; - if (atomic_read(&server->sem.count) == 1) - { - printk("smb_offerconn: server not locked, count=%d\n", - atomic_read(&server->sem.count)); -#if 0 - goto out; -#endif - } - - server->conn_pid = current->pid; - server->state = CONN_VALID; - wake_up_interruptible(&server->wait); -#ifdef SMBFS_PARANOIA -printk("smb_offerconn: state valid, pid=%d\n", server->conn_pid); -#endif - error = 0; - -out: - return error; -} - -/* - * This must be called with the server locked. - * N.B. The first call is made without locking the server -- need to fix! + * This implements the NEWCONN ioctl. It installs the server pid, + * sets server->state to CONN_VALID, and wakes up the waiting process. + * + * Note that this must be called with the server locked, except for + * the first call made after mounting the volume. The server pid + * will be set to zero to indicate that smbfs is awaiting a connection. */ int smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt) @@ -643,8 +617,13 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt) struct file *filp; int error; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_newconn: fd=%d, pid=%d\n", opt->fd, current->pid); +#endif error = -EBADF; - if (opt->fd >= NR_OPEN || !(filp = current->files->fd[opt->fd])) + if (opt->fd < 0 || opt->fd >= NR_OPEN) + goto out; + if (!(filp = current->files->fd[opt->fd])) goto out; if (!smb_valid_socket(filp->f_dentry->d_inode)) goto out; @@ -652,19 +631,22 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt) error = -EACCES; if ((current->uid != server->mnt->mounted_uid) && !suser()) goto out; - if (atomic_read(&server->sem.count) == 1) + + /* + * Make sure we don't already have a pid ... + */ + error = -EINVAL; + if (server->conn_pid) { - printk("smb_newconn: server not locked, count=%d\n", - atomic_read(&server->sem.count)); -#if 0 + printk("SMBFS: invalid ioctl call\n"); goto out; -#endif } + server->conn_pid = current->pid; - /* - * Make sure the old socket is closed - */ - smb_close_socket(server); +#ifdef SMBFS_PARANOIA +if (server->sock_file) +printk("smb_newconn: old socket not closed!\n"); +#endif filp->f_count += 1; server->sock_file = filp; @@ -675,9 +657,14 @@ printk("smb_newconn: protocol=%d, max_xmit=%d\n", server->opt.protocol, server->opt.max_xmit); #endif server->generation += 1; + server->state = CONN_VALID; +#ifdef SMBFS_PARANOIA +printk("smb_newconn: state valid, pid=%d\n", server->conn_pid); +#endif error = 0; out: + wake_up_interruptible(&server->wait); return error; } @@ -774,13 +761,14 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish) if (mode == read_write && (error == -EACCES || error == -ETXTBSY || error == -EROFS)) { -#ifdef SMBFS_PARANOIA +#ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_open: %s/%s R/W failed, error=%d, retrying R/O\n", dentry->d_parent->d_name.name, dentry->d_name.name, error); #endif mode = read_only; goto retry; } + goto out; } /* We should now have data in vwv[0..6]. */ @@ -788,17 +776,10 @@ dentry->d_parent->d_name.name, dentry->d_name.name, error); ino->u.smbfs_i.attr = WVAL(server->packet, smb_vwv1); /* smb_vwv2 has mtime */ /* smb_vwv4 has size */ - ino->u.smbfs_i.access = WVAL(server->packet, smb_vwv6); - ino->u.smbfs_i.access &= SMB_ACCMASK; - - /* N.B. Suppose the open failed?? */ + ino->u.smbfs_i.access = (WVAL(server->packet, smb_vwv6) & SMB_ACCMASK); ino->u.smbfs_i.open = server->generation; -#ifdef SMBFS_PARANOIA -if (error) -printk("smb_proc_open: %s/%s failed, error=%d, access=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name,error,ino->u.smbfs_i.access); -#endif +out: return error; } @@ -881,8 +862,8 @@ smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime) * * Win NT 4.0 has an apparent bug in that it fails to update the * modify time when writing to a file. As a workaround, we update - * the attributes if the file has been modified locally (we want to - * keep modify and access times in sync ...) + * both modify and access time locally, and post the times to the + * server when closing the file. */ static int smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino) @@ -891,33 +872,32 @@ smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino) if (smb_is_open(ino)) { /* - * Check whether to update locally-modified attributes at - * closing time. This is necessary to keep the modify and - * access times in sync. - * - * Kludge alert: If we're using trans2 getattr messages, - * the timestamps are accurate only to two seconds ... - * we must round the time to avoid cache invalidations! + * We clear the open flag in advance, in case another + * process observes the value while we block below. */ - if (ino->u.smbfs_i.access == SMB_O_RDWR || - ino->u.smbfs_i.access == SMB_O_WRONLY) { - struct smb_fattr fattr; + ino->u.smbfs_i.open = 0; - if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) { - if (ino->i_mtime & 1) - ino->i_mtime--; - if (ino->i_atime & 1) - ino->i_atime--; - } + /* + * Kludge alert: SMB timestamps are accurate only to + * two seconds ... round the times to avoid needless + * cache invalidations! + */ + if (ino->i_mtime & 1) + ino->i_mtime--; + if (ino->i_atime & 1) + ino->i_atime--; + /* + * If the file is open with write permissions, + * update the time stamps to sync mtime and atime. + */ + if ((server->opt.protocol >= SMB_PROTOCOL_LANMAN2) && + !(ino->u.smbfs_i.access == SMB_O_RDONLY)) + { + struct smb_fattr fattr; smb_get_inode_attr(ino, &fattr); - smb_proc_setfile_trans2(server, ino, &fattr); + smb_proc_setattr_ext(server, ino, &fattr); } - /* - * We clear the open flag in advance, in case another - * process observes the value while we block below. - */ - ino->u.smbfs_i.open = 0; result = smb_proc_close(server, ino->u.smbfs_i.fileid, ino->i_mtime); ino->u.smbfs_i.cache_valid &= ~SMB_F_LOCALWRITE; @@ -1082,14 +1062,12 @@ count, offset, server->packet_size); } int -smb_proc_create(struct dentry *dir, struct qstr *name, - __u16 attr, time_t ctime, __u16 *fileid) +smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid) { - struct smb_sb_info *server; + struct smb_sb_info *server = server_from_dentry(dentry); char *p; int error; - server = server_from_dentry(dir); smb_lock_server(server); retry: @@ -1097,7 +1075,7 @@ smb_proc_create(struct dentry *dir, struct qstr *name, WSET(server->packet, smb_vwv0, attr); DSET(server->packet, smb_vwv1, utc2local(ctime)); *p++ = 4; - p = smb_encode_path(server, p, dir, name); + p = smb_encode_path(server, p, dentry, NULL); smb_setup_bcc(server, p); error = smb_request_ok(server, SMBcreate, 1, 0); @@ -1116,23 +1094,21 @@ out: } int -smb_proc_mv(struct dentry *odir, struct qstr *oname, - struct dentry *ndir, struct qstr *nname) +smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry) { - struct smb_sb_info *server; + struct smb_sb_info *server = server_from_dentry(old_dentry); char *p; int result; - server = server_from_dentry(odir); smb_lock_server(server); retry: p = smb_setup_header(server, SMBmv, 1, 0); WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN); *p++ = 4; - p = smb_encode_path(server, p, odir, oname); + p = smb_encode_path(server, p, old_dentry, NULL); *p++ = 4; - p = smb_encode_path(server, p, ndir, nname); + p = smb_encode_path(server, p, new_dentry, NULL); smb_setup_bcc(server, p); if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0) @@ -1147,23 +1123,26 @@ out: return result; } -int -smb_proc_mkdir(struct dentry *dir, struct qstr *name) +/* + * Code common to mkdir and rmdir. + */ +static int +smb_proc_generic_command(struct dentry *dentry, __u8 command) { - struct smb_sb_info *server; + struct smb_sb_info *server = server_from_dentry(dentry); char *p; int result; - server = server_from_dentry(dir); smb_lock_server(server); retry: - p = smb_setup_header(server, SMBmkdir, 0, 0); + p = smb_setup_header(server, command, 0, 0); *p++ = 4; - p = smb_encode_path(server, p, dir, name); + p = smb_encode_path(server, p, dentry, NULL); smb_setup_bcc(server, p); - if ((result = smb_request_ok(server, SMBmkdir, 0, 0)) < 0) + result = smb_request_ok(server, command, 0, 0); + if (result < 0) { if (smb_retry(server)) goto retry; @@ -1176,48 +1155,31 @@ out: } int -smb_proc_rmdir(struct dentry *dir, struct qstr *name) +smb_proc_mkdir(struct dentry *dentry) { - struct smb_sb_info *server; - char *p; - int result; - - server = server_from_dentry(dir); - smb_lock_server(server); - - retry: - p = smb_setup_header(server, SMBrmdir, 0, 0); - *p++ = 4; - p = smb_encode_path(server, p, dir, name); - smb_setup_bcc(server, p); + return smb_proc_generic_command(dentry, SMBmkdir); +} - if ((result = smb_request_ok(server, SMBrmdir, 0, 0)) < 0) - { - if (smb_retry(server)) - goto retry; - goto out; - } - result = 0; -out: - smb_unlock_server(server); - return result; +int +smb_proc_rmdir(struct dentry *dentry) +{ + return smb_proc_generic_command(dentry, SMBrmdir); } int -smb_proc_unlink(struct dentry *dir, struct qstr *name) +smb_proc_unlink(struct dentry *dentry) { - struct smb_sb_info *server; + struct smb_sb_info *server = server_from_dentry(dentry); char *p; int result; - server = server_from_dentry(dir); smb_lock_server(server); retry: p = smb_setup_header(server, SMBunlink, 1, 0); WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN); *p++ = 4; - p = smb_encode_path(server, p, dir, name); + p = smb_encode_path(server, p, dentry, NULL); smb_setup_bcc(server, p); if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0) @@ -1276,17 +1238,15 @@ smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr) static void smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr) { + fattr->f_mode = server->mnt->file_mode; if (fattr->attr & aDIR) { - /* N.B. check for read-only directories */ fattr->f_mode = server->mnt->dir_mode; fattr->f_size = 512; - } else - { - fattr->f_mode = server->mnt->file_mode; - if (fattr->attr & aRONLY) - fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); } + /* Check the read-only flag */ + if (fattr->attr & aRONLY) + fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); fattr->f_blocks = 0; if ((fattr->f_blksize != 0) && (fattr->f_size != 0)) @@ -1302,7 +1262,7 @@ smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr) { smb_init_dirent(server, fattr); fattr->attr = aDIR; - fattr->f_ino = 1; + fattr->f_ino = 2; /* traditional root inode number */ fattr->f_mtime = CURRENT_TIME; smb_finish_dirent(server, fattr); } @@ -1475,11 +1435,17 @@ entries_seen, i, fpos); * Interpret a long filename structure using the specified info level: * level 1 -- Win NT, Win 95, OS/2 * level 259 -- File name and length only, Win NT, Win 95 - * There seem to be numerous inconsistencies and bugs in implementation. * * We return a reference to the name string to avoid copying, and perform * any needed upper/lower casing in place. Note!! Level 259 entries may * not have any space beyond the name, so don't try to write a null byte! + * + * Bugs Noted: + * (1) Win NT 4.0 appends a null byte to names and counts it in the length! + * (2) When using Info Level 1 Win NT 4.0 truncates directory listings + * for certain patterns of names and/or lengths. The breakage pattern is + * completely reproducible and can be toggled by the addition of a single + * file to the directory. (E.g. echo hi >foo breaks, rm -f foo works.) */ static char * smb_decode_long_dirent(struct smb_sb_info *server, char *p, @@ -1791,7 +1757,7 @@ smb_proc_readdir(struct dentry *dir, int fpos, void *cachep) */ static int smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, - struct qstr *name, struct smb_fattr *fattr) + struct smb_fattr *fattr) { int result; char *p; @@ -1799,7 +1765,7 @@ smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, retry: p = smb_setup_header(server, SMBgetatr, 0, 0); *p++ = 4; - p = smb_encode_path(server, p, dir, name); + p = smb_encode_path(server, p, dir, NULL); smb_setup_bcc(server, p); if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0) @@ -1825,10 +1791,13 @@ out: /* * Note: called with the server locked. + * + * Bugs Noted: + * (1) Win 95 swaps the date and time fields in the standard info level. */ static int smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, - struct qstr *name, struct smb_fattr *attr) + struct smb_fattr *attr) { char *p; int result; @@ -1843,7 +1812,7 @@ smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, retry: WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ DSET(param, 2, 0); - p = smb_encode_path(server, param + 6, dir, name); + p = smb_encode_path(server, param + 6, dir, NULL); result = smb_trans2_request(server, TRANSACT2_QPATHINFO, 0, NULL, p - param, param, @@ -1906,8 +1875,7 @@ out: } int -smb_proc_getattr(struct dentry *dir, struct qstr *name, - struct smb_fattr *fattr) +smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr) { struct smb_sb_info *server = server_from_dentry(dir); int result; @@ -1921,9 +1889,9 @@ smb_proc_getattr(struct dentry *dir, struct qstr *name, */ if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 && !(server->mnt->version & SMB_FIX_OLDATTR)) - result = smb_proc_getattr_trans2(server, dir, name, fattr); + result = smb_proc_getattr_trans2(server, dir, fattr); else - result = smb_proc_getattr_core(server, dir, name, fattr); + result = smb_proc_getattr_core(server, dir, fattr); smb_finish_dirent(server, fattr); @@ -1932,35 +1900,42 @@ smb_proc_getattr(struct dentry *dir, struct qstr *name, } /* - * In the core protocol there is only one time to be set, - * so we use fattr->f_mtime to make `touch' work. - * Note: called with the server locked. + * Called with the server locked. Because of bugs in the + * core protocol, we use this only to set attributes. See + * smb_proc_settime() below for timestamp handling. + * + * Bugs Noted: + * (1) If mtime is non-zero, both Win 3.1 and Win 95 fail + * with an undocumented error (ERRDOS code 50). Setting + * mtime to 0 allows the attributes to be set. + * (2) The extra parameters following the name string aren't + * in the CIFS docs, but seem to be necessary for operation. */ static int -smb_proc_setattr_core(struct smb_sb_info *server, - struct dentry *dir, struct smb_fattr *fattr) +smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, + __u16 attr) { char *p; - char *buf; int result; -#ifdef SMBFS_DEBUG_TIMESTAMP -printk("setattr_core: %s/%s, mtime=%ld\n", -dir->d_parent->d_name.name, dir->d_name.name, fattr->f_mtime); -#endif - retry: - buf = server->packet; p = smb_setup_header(server, SMBsetatr, 8, 0); - WSET(buf, smb_vwv0, fattr->attr); - DSET(buf, smb_vwv1, utc2local(fattr->f_mtime)); - WSET(buf, smb_vwv3, 0); /* reserved values */ - WSET(buf, smb_vwv4, 0); - WSET(buf, smb_vwv5, 0); - WSET(buf, smb_vwv6, 0); - WSET(buf, smb_vwv7, 0); + WSET(server->packet, smb_vwv0, attr); + DSET(server->packet, smb_vwv1, 0); /* mtime */ + WSET(server->packet, smb_vwv3, 0); /* reserved values */ + WSET(server->packet, smb_vwv4, 0); + WSET(server->packet, smb_vwv5, 0); + WSET(server->packet, smb_vwv6, 0); + WSET(server->packet, smb_vwv7, 0); *p++ = 4; - p = smb_encode_path(server, p, dir, NULL); + /* + * Samba uses three leading '\', so we'll do it too. + */ + *p++ = '\\'; + *p++ = '\\'; + p = smb_encode_path(server, p, dentry, NULL); + *p++ = 4; + *p++ = 0; smb_setup_bcc(server, p); result = smb_request_ok(server, SMBsetatr, 0, 0); @@ -1976,50 +1951,55 @@ out: } /* - * Note: called with the server locked. + * Because of bugs in the trans2 setattr messages, we must set + * attributes and timestamps separately. The core SMBsetatr + * message seems to be the only reliable way to set attributes. + */ +int +smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr) +{ + struct smb_sb_info *server = server_from_dentry(dir); + int result; + +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_proc_setattr: setting %s/%s, open=%d\n", +dir->d_parent->d_name.name, dir->d_name.name, smb_is_open(dir->d_inode)); +#endif + smb_lock_server(server); + result = smb_proc_setattr_core(server, dir, fattr->attr); + smb_unlock_server(server); + return result; +} + +/* + * Called with the server locked. Sets the timestamps for an + * file open with write permissions. */ static int -smb_proc_setattr_trans2(struct smb_sb_info *server, - struct dentry *dir, struct smb_fattr *fattr) +smb_proc_setattr_ext(struct smb_sb_info *server, + struct inode *inode, struct smb_fattr *fattr) { __u16 date, time; - char *p; int result; - unsigned char *resp_data = NULL; - unsigned char *resp_param = NULL; - int resp_data_len = 0; - int resp_param_len = 0; - char param[SMB_MAXPATHLEN + 20]; /* too long for the stack! */ - char data[26]; - retry: - WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ - DSET(param, 2, 0); - p = smb_encode_path(server, param + 6, dir, NULL); - - date_unix2dos(fattr->f_ctime, &date, &time); - WSET(data, 0, date); - WSET(data, 2, time); + smb_setup_header(server, SMBsetattrE, 7, 0); + WSET(server->packet, smb_vwv0, inode->u.smbfs_i.fileid); + /* We don't change the creation time */ + WSET(server->packet, smb_vwv1, 0); + WSET(server->packet, smb_vwv2, 0); date_unix2dos(fattr->f_atime, &date, &time); - WSET(data, 4, date); - WSET(data, 6, time); + WSET(server->packet, smb_vwv3, date); + WSET(server->packet, smb_vwv4, time); date_unix2dos(fattr->f_mtime, &date, &time); - WSET(data, 8, date); - WSET(data, 10, time); + WSET(server->packet, smb_vwv5, date); + WSET(server->packet, smb_vwv6, time); #ifdef SMBFS_DEBUG_TIMESTAMP -printk("setattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", -dir->d_parent->d_name.name, dir->d_name.name, date, time, fattr->f_mtime); +printk("smb_proc_setattr_ext: date=%d, time=%d, mtime=%ld\n", +date, time, fattr->f_mtime); #endif - DSET(data, 12, fattr->f_size); - DSET(data, 16, fattr->f_blksize); - WSET(data, 20, fattr->attr); - DSET(data, 22, 0); /* ULONG EA size */ - result = smb_trans2_request(server, TRANSACT2_SETPATHINFO, - 26, data, p - param, param, - &resp_data_len, &resp_data, - &resp_param_len, &resp_param); + result = smb_request_ok(server, SMBsetattrE, 0, 0); if (result < 0) { if (smb_retry(server)) @@ -2027,54 +2007,58 @@ dir->d_parent->d_name.name, dir->d_name.name, date, time, fattr->f_mtime); goto out; } result = 0; - if (server->rcls != 0) - result = -smb_errno(server); - out: return result; } /* - * Set the attributes for an open file. + * Note: called with the server locked. + * + * Bugs Noted: + * (1) The TRANSACT2_SETPATHINFO message under Win NT 4.0 doesn't + * set the file's attribute flags. */ static int -smb_proc_setfile_trans2(struct smb_sb_info *server, - struct inode * inode, struct smb_fattr *fattr) +smb_proc_setattr_trans2(struct smb_sb_info *server, + struct dentry *dir, struct smb_fattr *fattr) { __u16 date, time; + char *p; + int result; + unsigned char *resp_data = NULL; - unsigned char *resp_parm = NULL; + unsigned char *resp_param = NULL; int resp_data_len = 0; - int resp_parm_len = 0; - int result; - char parm[6], data[26]; + int resp_param_len = 0; + char param[SMB_MAXPATHLEN + 20]; /* too long for the stack! */ + char data[26]; retry: - WSET(parm, 0, inode->u.smbfs_i.fileid); - WSET(parm, 2, 1); /* Info level SMB_INFO_STANDARD */ + WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ + DSET(param, 2, 0); + p = smb_encode_path(server, param + 6, dir, NULL); - date_unix2dos(fattr->f_ctime, &date, &time); - WSET(data, 0, date); - WSET(data, 2, time); + WSET(data, 0, 0); /* creation time */ + WSET(data, 2, 0); date_unix2dos(fattr->f_atime, &date, &time); WSET(data, 4, date); WSET(data, 6, time); -#ifdef SMBFS_DEBUG_TIMESTAMP -printk("smb_proc_setfile_trans2: date=%x, time=%x, atime=%ld\n", -date, time, fattr->f_atime); -#endif date_unix2dos(fattr->f_mtime, &date, &time); WSET(data, 8, date); WSET(data, 10, time); - DSET(data, 12, fattr->f_size); - DSET(data, 16, fattr->f_blksize); - WSET(data, 20, fattr->attr); +#ifdef SMBFS_DEBUG_TIMESTAMP +printk("setattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", +dir->d_parent->d_name.name, dir->d_name.name, date, time, fattr->f_mtime); +#endif + DSET(data, 12, 0); /* size */ + DSET(data, 16, 0); /* blksize */ + WSET(data, 20, 0); /* attr */ DSET(data, 22, 0); /* ULONG EA size */ - result = smb_trans2_request(server, TRANSACT2_SETFILEINFO, - 26, data, 6, parm, + result = smb_trans2_request(server, TRANSACT2_SETPATHINFO, + 26, data, p - param, param, &resp_data_len, &resp_data, - &resp_parm_len, &resp_parm); + &resp_param_len, &resp_param); if (result < 0) { if (smb_retry(server)) @@ -2089,27 +2073,70 @@ out: return result; } +/* + * Set the modify and access timestamps for a file. + * + * Incredibly enough, in all of SMB there is no message to allow + * setting both attributes and timestamps at once. + * + * Bugs Noted: + * (1) Win 95 doesn't support the TRANSACT2_SETFILEINFO message + * with info level 1 (INFO_STANDARD). + * (2) Under the core protocol apparently the only way to set the + * timestamp is to open and close the file. + */ int -smb_proc_setattr(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr) +smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr) { + struct smb_sb_info *server = server_from_dentry(dentry); + struct inode *inode = dentry->d_inode; int result; -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_proc_setattr: setting %s/%s, open=%d\n", -dir->d_parent->d_name.name, dir->d_name.name, smb_is_open(dir->d_inode)); +#ifdef SMBFS_DEBUG_TIMESTAMP +printk("smb_proc_settime: setting %s/%s, open=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode)); #endif smb_lock_server(server); - if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) { - struct inode *inode = dir->d_inode; - - if (smb_is_open(inode)) - result = smb_proc_setfile_trans2(server, inode, fattr); + if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) + { + if (smb_is_open(inode) && + inode->u.smbfs_i.access != SMB_O_RDONLY) + result = smb_proc_setattr_ext(server, inode, fattr); else - result = smb_proc_setattr_trans2(server, dir, fattr); + result = smb_proc_setattr_trans2(server, dentry, fattr); } else - result = smb_proc_setattr_core(server, dir, fattr); + { + /* + * Fail silently on directories ... timestamp can't be set? + */ + result = 0; + if (S_ISREG(inode->i_mode)) + { + /* + * Set the mtime by opening and closing the file. + */ + result = -EACCES; + if (!smb_is_open(inode)) + smb_proc_open(server, dentry, SMB_O_WRONLY); + if (smb_is_open(inode) && + inode->u.smbfs_i.access != SMB_O_RDONLY) + { + inode->i_mtime = fattr->f_mtime; + result = smb_proc_close_inode(server, inode); + } + } + } +#if 1 /* temporary */ + if (result) + { +printk("smb_proc_settime: %s/%s failed, open=%d, res=%d, rcls=%d, err=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode), +result, server->rcls, server->err); + /* squash errors for now */ + result = 0; + } +#endif smb_unlock_server(server); return result; } @@ -2133,8 +2160,8 @@ smb_proc_dskattr(struct super_block *sb, struct statfs *attr) goto out; } p = SMB_VWV(server->packet); - attr->f_bsize = WVAL(p, 2) * WVAL(p, 4); attr->f_blocks = WVAL(p, 0); + attr->f_bsize = WVAL(p, 2) * WVAL(p, 4); attr->f_bavail = attr->f_bfree = WVAL(p, 6); error = 0; diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c index 45a0790fc..0803a483f 100644 --- a/fs/smbfs/sock.c +++ b/fs/smbfs/sock.c @@ -397,10 +397,6 @@ smb_receive(struct smb_sb_info *server) { int new_len = smb_round_length(len + 4); -#ifdef SMBFS_PARANOIA -printk("smb_receive: Increase packet size from %d to %d\n", -server->packet_size, new_len); -#endif result = -ENOMEM; packet = smb_vmalloc(new_len); if (packet == NULL) @@ -655,6 +651,17 @@ smb_request(struct smb_sb_info *server) } if (result < 0) goto bad_conn; + /* + * Check for fatal server errors ... + */ + if (server->rcls) { + int error = smb_errno(server); + if (error == EBADSLT) { + printk("smb_request: tree ID invalid\n"); + result = error; + goto bad_conn; + } + } out: pr_debug("smb_request: result = %d\n", result); @@ -827,6 +834,17 @@ smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command, } if (result < 0) goto bad_conn; + /* + * Check for fatal server errors ... + */ + if (server->rcls) { + int error = smb_errno(server); + if (error == EBADSLT) { + printk("smb_request: tree ID invalid\n"); + result = error; + goto bad_conn; + } + } out: return result; diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index bb8828efa..ff3d7c7af 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -9,10 +9,10 @@ * * sysv/namei.c * Copyright (C) 1993 Bruno Haible - */ -/* - 7 Dec 1997 - updated to use dentries by Krzysztof G. Baranowski - + * + * + * Revised: 15 Dec 1997 by Krzysztof G. Baranowski + * Driver updated to use dentries. */ @@ -107,7 +107,6 @@ static struct buffer_head * sysv_find_entry(struct inode * dir, int sysv_lookup(struct inode * dir, struct dentry * dentry) { - int ino; struct inode * inode = NULL; struct sysv_dir_entry * de; struct buffer_head * bh; @@ -117,16 +116,16 @@ int sysv_lookup(struct inode * dir, struct dentry * dentry) if (!S_ISDIR(dir->i_mode)) { return -ENOENT; } - if (!(bh = sysv_find_entry(dir, dentry->d_name.name, - dentry->d_name.len, &de))) { - return -ENOENT; - } - ino = de->inode; - brelse(bh); - inode = iget(dir->i_sb,ino); + bh = sysv_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); + + if (bh) { + int ino = de->inode; + brelse(bh); + inode = iget(dir->i_sb,ino); - if (!inode) - return -EACCES; + if (!inode) + return -EACCES; + } d_add(dentry, inode); return 0; } @@ -302,9 +301,8 @@ int sysv_mkdir(struct inode * dir, struct dentry *dentry, int mode) brelse(bh); return -EEXIST; } - if (dir->i_nlink >= dir->i_sb->sv_link_max) { + if (dir->i_nlink >= dir->i_sb->sv_link_max) return -EMLINK; - } inode = sysv_new_inode(dir); if (!inode) return -ENOSPC; @@ -595,6 +593,7 @@ int sysv_link(struct inode * oldinode, struct inode * dir, error = sysv_add_entry(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de); if (error) { + brelse(bh); return error; } de->inode = oldinode->i_ino; @@ -652,8 +651,6 @@ try_again: brelse(old_bh); brelse(new_bh); brelse(dir_bh); - iput(old_inode); - iput(new_inode); current->counter = 0; schedule(); start_up: @@ -759,15 +756,12 @@ start_up: mark_inode_dirty(new_dir); } } + d_move(old_dentry, new_dentry); retval = 0; end_rename: brelse(dir_bh); brelse(old_bh); brelse(new_bh); - iput(old_inode); - iput(new_inode); - iput(old_dir); - iput(new_dir); return retval; } -- cgit v1.2.3