patch-2.1.73 linux/fs/smbfs/proc.c
Next file: linux/fs/smbfs/sock.c
Previous file: linux/fs/smbfs/ioctl.c
Back to the patch index
Back to the overall index
- Lines: 962
- Date:
Thu Dec 11 11:25:54 1997
- Orig file:
v2.1.72/linux/fs/smbfs/proc.c
- Orig date:
Wed Nov 26 16:24:03 1997
diff -u --recursive --new-file v2.1.72/linux/fs/smbfs/proc.c linux/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 = nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9);
}
-
/*****************************************************************************/
/* */
/* Support section. */
@@ -349,7 +347,7 @@
return size;
}
-static int
+int
smb_errno(struct smb_sb_info *server)
{
int errcls = server->rcls;
@@ -402,7 +400,7 @@
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 @@
} else if (errcls == ERRSRV)
switch (error)
{
+ /* N.B. This is wrong ... EIO ? */
case ERRerror:
return ENFILE;
case ERRbadpw:
@@ -421,6 +420,13 @@
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 @@
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 @@
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 @@
}
/*
- * 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
*
* 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 @@
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 @@
}
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 @@
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 @@
}
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 @@
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 @@
}
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 @@
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_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 @@
* 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 @@
*/
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 @@
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 @@
/*
* 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 @@
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 @@
}
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 @@
*/
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 @@
}
/*
- * 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 @@
}
/*
- * 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 @@
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 @@
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 @@
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;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov