diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-03-03 01:22:27 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-03-03 01:22:27 +0000 |
commit | f9bbe9da79dbc8557c74efeb158b431cd67ace52 (patch) | |
tree | 3220d014a35f9d88a48668a1468524e988daebff /fs/smbfs | |
parent | 3d697109c1ff85ef563aec3d5e113ef225ed2792 (diff) |
Upgrade to 2.1.73.
Diffstat (limited to 'fs/smbfs')
-rw-r--r-- | fs/smbfs/dir.c | 174 | ||||
-rw-r--r-- | fs/smbfs/file.c | 4 | ||||
-rw-r--r-- | fs/smbfs/inode.c | 138 | ||||
-rw-r--r-- | fs/smbfs/ioctl.c | 21 | ||||
-rw-r--r-- | fs/smbfs/proc.c | 563 | ||||
-rw-r--r-- | fs/smbfs/sock.c | 26 |
6 files changed, 535 insertions, 391 deletions
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; |