summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-03-03 01:22:27 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-03-03 01:22:27 +0000
commitf9bbe9da79dbc8557c74efeb158b431cd67ace52 (patch)
tree3220d014a35f9d88a48668a1468524e988daebff /fs
parent3d697109c1ff85ef563aec3d5e113ef225ed2792 (diff)
Upgrade to 2.1.73.
Diffstat (limited to 'fs')
-rw-r--r--fs/dcache.c36
-rw-r--r--fs/ext2/fsync.c197
-rw-r--r--fs/fat/inode.c10
-rw-r--r--fs/isofs/dir.c28
-rw-r--r--fs/isofs/inode.c58
-rw-r--r--fs/isofs/rock.c1
-rw-r--r--fs/msdos/namei.c101
-rw-r--r--fs/namei.c25
-rw-r--r--fs/nfs/dir.c75
-rw-r--r--fs/nfs/inode.c16
-rw-r--r--fs/nfs/write.c7
-rw-r--r--fs/nfsd/vfs.c141
-rw-r--r--fs/proc/generic.c2
-rw-r--r--fs/smbfs/dir.c174
-rw-r--r--fs/smbfs/file.c4
-rw-r--r--fs/smbfs/inode.c138
-rw-r--r--fs/smbfs/ioctl.c21
-rw-r--r--fs/smbfs/proc.c563
-rw-r--r--fs/smbfs/sock.c26
-rw-r--r--fs/sysv/namei.c38
20 files changed, 1057 insertions, 604 deletions
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;p<rr->u.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);
@@ -516,10 +516,38 @@ out:
}
/*
+ * 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
{
/*
@@ -245,21 +282,49 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
}
/*
+ * 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.
*/
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,13 +122,22 @@ 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.
*/
inode->u.smbfs_i.oldmtime = jiffies;
@@ -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 <linux/ioctl.h>
#include <linux/sched.h>
#include <linux/mm.h>
+
#include <linux/smb_fs.h>
#include <linux/smb_mount.h>
@@ -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
- <kgb@manjak.knm.org.pl>
+ *
+ *
+ * Revised: 15 Dec 1997 by Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+ * 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;
}