r7172: This is the proper fix for setting file times from libsmbclient. We now
authorDerrell Lipman <derrell@samba.org>
Wed, 1 Jun 2005 20:17:16 +0000 (20:17 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 15:57:04 +0000 (10:57 -0500)
try setpathinfo, and if that doesn't work (e.g. on win98), revert to the
previous slower method.
(This used to be commit 6c05812bd90b0db69d974ee2758721dc2974a507)

source3/include/libsmb_internal.h
source3/libsmb/clirap.c
source3/libsmb/libsmb_cache.c
source3/libsmb/libsmbclient.c

index 2eca879cbe2a03f76bc8ceba98d1c98228fbd2e9..081bb415e560224db0599f6b5aada4db34f918e6 100644 (file)
@@ -12,9 +12,9 @@
 struct _SMBCSRV {
        struct cli_state cli;
        dev_t dev;
+       BOOL no_pathinfo;
        BOOL no_pathinfo2;
         BOOL no_nt_session;
-       int server_fd;
 
        SMBCSRV *next, *prev;
        
index 06b683b0385df18811808059ec8acb9dc6a797d8..4766811c8e8d54e922a5ab72a4a64845ec26c8d9 100644 (file)
@@ -457,6 +457,101 @@ BOOL cli_qpathinfo(struct cli_state *cli, const char *fname,
        return True;
 }
 
+
+/****************************************************************************
+send a setpathinfo call
+****************************************************************************/
+BOOL cli_setpathinfo(struct cli_state *cli, const char *fname, 
+                     time_t c_time, time_t a_time, time_t m_time, uint16 mode)
+{
+       unsigned int data_len = 0;
+       unsigned int param_len = 0;
+       unsigned int rparam_len, rdata_len;
+       uint16 setup = TRANSACT2_SETPATHINFO;
+       pstring param;
+        pstring data;
+       char *rparam=NULL, *rdata=NULL;
+       int count=8;
+       BOOL ret;
+        void (*date_fn)(char *buf,int offset,time_t unixdate);
+       char *p;
+
+       memset(param, 0, sizeof(param));
+       memset(data, 0, sizeof(data));
+
+        p = param;
+
+        /* Add the information level */
+       SSVAL(p, 0, SMB_INFO_STANDARD);
+
+        /* Skip reserved */
+       p += 6;
+
+        /* Add the file name */
+       p += clistr_push(cli, p, fname, sizeof(pstring)-6, STR_TERMINATE);
+
+       param_len = PTR_DIFF(p, param);
+
+        p = data;
+
+       if (cli->win95) {
+               date_fn = put_dos_date;
+       } else {
+               date_fn = put_dos_date2;
+       }
+
+        /* Add the create, last access, and modification times */
+        (*date_fn)(p, 0, c_time);
+        (*date_fn)(p, 4, a_time);
+        (*date_fn)(p, 8, m_time);
+        p += 12;
+
+        /* Skip DataSize and AllocationSize */
+        p += 8;
+
+        /* Add attributes */
+        SSVAL(p, 0, mode);
+        p += 2;
+
+        /* Add EA size (none) */
+        SIVAL(p, 0, 0);
+        p += 4;
+
+        data_len = PTR_DIFF(p, data);
+
+       do {
+               ret = (cli_send_trans(cli, SMBtrans2, 
+                                     NULL,           /* Name */
+                                     -1, 0,          /* fid, flags */
+                                     &setup, 1, 0,   /* setup, length, max */
+                                     param, param_len, 10, /* param, length, max */
+                                     data, data_len, cli->max_xmit /* data, length, max */
+                                     ) &&
+                      cli_receive_trans(cli, SMBtrans2, 
+                                        &rparam, &rparam_len,
+                                        &rdata, &rdata_len));
+               if (!cli_is_dos_error(cli)) break;
+               if (!ret) {
+                       /* we need to work around a Win95 bug - sometimes
+                          it gives ERRSRV/ERRerror temprarily */
+                       uint8 eclass;
+                       uint32 ecode;
+                       cli_dos_error(cli, &eclass, &ecode);
+                       if (eclass != ERRSRV || ecode != ERRerror) break;
+                       smb_msleep(100);
+               }
+       } while (count-- && ret==False);
+
+       if (!ret) {
+               return False;
+       }
+
+       SAFE_FREE(rdata);
+       SAFE_FREE(rparam);
+       return True;
+}
+
+
 /****************************************************************************
 send a qpathinfo call with the SMB_QUERY_FILE_ALL_INFO info level
 ****************************************************************************/
index dabf5a527d5040523a43546e79e545c04294dabe..de9a1656d84f1acb113e5b7749b589aab9bfc205 100644 (file)
@@ -102,7 +102,7 @@ static int smbc_add_cached_server(SMBCCTX * context, SMBCSRV * new,
 
 /*
  * Search the server cache for a server 
- * returns server_fd on success, -1 on error (not found)
+ * returns server handle on success, NULL on error (not found)
  * This function is only used if the external cache is not enabled 
  */
 static SMBCSRV * smbc_get_cached_server(SMBCCTX * context, const char * server, 
index 296dbc7f5bb7c5180766b6f33538b15078ff42a5..404d9def69698f16b0e0d5f516624981ef534918 100644 (file)
@@ -420,7 +420,7 @@ static int smbc_errno(SMBCCTX *context, struct cli_state *c)
 }
 
 /* 
- * Check a server_fd.
+ * Check a server for being alive and well.
  * returns 0 if the server is in shape. Returns 1 on error 
  * 
  * Also useable outside libsmbclient to enable external cache
@@ -745,7 +745,7 @@ SMBCSRV *smbc_server(SMBCCTX *context,
   
        /*
         * Ok, we have got a nice connection
-        * Let's find a free server_fd 
+        * Let's allocate a server structure.
         */
 
        srv = SMB_MALLOC_P(SMBCSRV);
@@ -757,6 +757,9 @@ SMBCSRV *smbc_server(SMBCCTX *context,
        ZERO_STRUCTP(srv);
        srv->cli = c;
        srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
+        srv->no_pathinfo = False;
+        srv->no_pathinfo2 = False;
+        srv->no_nt_session = False;
 
        /* now add it to the cache (internal or external)  */
        /* Let the cache function set errno if it wants to */
@@ -1259,10 +1262,133 @@ static BOOL smbc_getatr(SMBCCTX * context, SMBCSRV *srv, char *path,
 }
 
 /*
- * Routine to unlink() a file
+ * Set file info on an SMB server.  Use setpathinfo call first.  If that
+ * fails, use setattrE..
+ *
+ * Time parameters are always used and must be provided.
+ * "mode" (attributes) parameter may be set to -1 if it is not to be set.
  */
+static BOOL smbc_setatr(SMBCCTX * context, SMBCSRV *srv, char *path, 
+                        time_t c_time, time_t a_time, time_t m_time,
+                        uint16 mode)
+{
+        int fd;
+        int ret;
+
+        /*
+         * Get the create time of the file (if not provided); we'll need it in
+         * the set call.
+         */
+        if (! srv->no_pathinfo && c_time != 0) {
+                if (! cli_qpathinfo(&srv->cli, path,
+                                    &c_time, NULL, NULL, NULL, NULL)) {
+                        /* qpathinfo not available */
+                        srv->no_pathinfo = True;
+                } else {
+                        /*
+                         * We got a creation time.  For sanity sake, since
+                         * there is no POSIX function to set the create time
+                         * of a file, if the existing create time is greater
+                         * than either of access time or modification time,
+                         * set create time to the smallest of those.  This
+                         * ensure that the create time of a file is never
+                         * greater than its last access or modification time.
+                         */
+                        if (c_time > a_time) c_time = a_time;
+                        if (c_time > m_time) c_time = m_time;
+                }
+        }
+
+        /*
+         * First, try setpathinfo (if qpathinfo succeeded), for it is the
+         * modern function for "new code" to be using, and it works given a
+         * filename rather than requiring that the file be opened to have its
+         * attributes manipulated.
+         */
+        if (srv->no_pathinfo ||
+            ! cli_setpathinfo(&srv->cli, path, c_time, a_time, m_time, mode)) {
+
+                /*
+                 * setpathinfo is not supported; go to plan B. 
+                 *
+                 * cli_setatr() does not work on win98, and it also doesn't
+                 * support setting the access time (only the modification
+                 * time), so in all cases, we open the specified file and use
+                 * cli_setattrE() which should work on all OS versions, and
+                 * supports both times.
+                 */
 
-static int smbc_unlink_ctx(SMBCCTX *context, const char *fname)
+                /* Don't try {q,set}pathinfo() again, with this server */
+                srv->no_pathinfo = True;
+
+                /* Open the file */
+                if ((fd = cli_open(&srv->cli, path, O_RDWR, DENY_NONE)) < 0) {
+
+                        errno = smbc_errno(context, &srv->cli);
+                        return -1;
+                }
+
+                /*
+                 * Get the creat time of the file (if it wasn't provided).
+                 * We'll need it in the set call
+                 */
+                if (c_time == 0) {
+                        ret = cli_getattrE(&srv->cli, fd,
+                                           NULL, NULL,
+                                           &c_time, NULL, NULL);
+                } else {
+                        ret = True;
+                }
+                    
+                /* If we got create time, set times */
+                if (ret) {
+                        /* Some OS versions don't support create time */
+                        if (c_time == 0) {
+                                c_time = time(NULL);
+                        }
+
+                        /*
+                         * For sanity sake, since there is no POSIX function
+                         * to set the create time of a file, if the existing
+                         * create time is greater than either of access time
+                         * or modification time, set create time to the
+                         * smallest of those.  This ensure that the create
+                         * time of a file is never greater than its last
+                         * access or modification time.
+                         */
+                        if (c_time > a_time) c_time = a_time;
+                        if (c_time > m_time) c_time = m_time;
+                        
+                        /* Set the new attributes */
+                        ret = cli_setattrE(&srv->cli, fd,
+                                           c_time, a_time, m_time);
+                        cli_close(&srv->cli, fd);
+                }
+
+                /*
+                 * Unfortunately, setattrE() doesn't have a provision for
+                 * setting the access mode (attributes).  We'll have to try
+                 * cli_setatr() for that, and with only this parameter, it
+                 * seems to work on win98.
+                 */
+                if (ret && mode != (uint16) -1) {
+                        ret = cli_setatr(&srv->cli, path, mode, 0);
+                }
+
+                if (! ret) {
+                        errno = smbc_errno(context, &srv->cli);
+                        return False;
+                }
+        }
+
+        return True;
+}
+
+ /*
+  * Routine to unlink() a file
+  */
+
+ static int smbc_unlink_ctx(SMBCCTX *context, const char *fname)
 {
        fstring server, share, user, password, workgroup;
        pstring path;
@@ -2851,12 +2977,9 @@ int smbc_chmod_ctx(SMBCCTX *context, const char *fname, mode_t newmode)
 
 int smbc_utimes_ctx(SMBCCTX *context, const char *fname, struct timeval *tbuf)
 {
-        int fd;
-        int ret;
         SMBCSRV *srv;
        fstring server, share, user, password, workgroup;
        pstring path;
-        time_t c_time;
         time_t a_time;
         time_t m_time;
 
@@ -2910,49 +3033,14 @@ int smbc_utimes_ctx(SMBCCTX *context, const char *fname, struct timeval *tbuf)
        srv = smbc_server(context, server, share, workgroup, user, password);
 
        if (!srv) {
-               return -1;  /* errno set by smbc_server */
+               return -1;      /* errno set by smbc_server */
        }
 
-        /*
-         * cli_setatr() does not work on win98, and it also doesn't support
-         * setting the access time (only the modification time), so in all
-         * cases, we open the specified file and use cli_setattrE() which
-         * should work on all OS versions, and supports both times.
-         */
-        if ((fd = cli_open(&srv->cli, path, O_RDWR, DENY_NONE)) < 0) {
-
-                errno = smbc_errno(context, &srv->cli);
-                return -1;
-                
+        if (!smbc_setatr(context, srv, path, 0, a_time, m_time, 0)) {
+                return -1;      /* errno set by smbc_setatr */
         }
 
-        /* Get the creat time of the file; we'll need it in the set call */
-        ret = cli_getattrE(&srv->cli, fd, NULL, NULL, &c_time, NULL, NULL);
-
-        /* Some OS versions don't support create time */
-        if (c_time == 0) {
-                c_time = time(NULL);
-        }
-
-        /*
-         * For sanity sake, since there is no POSIX function to set the create
-         * time of a file, if the existing create time is greater than either
-         * of access time or modification time, set create time to the
-         * smallest of those.  This ensure that the create time of a file is
-         * never greater than its last access or modification time.
-         */
-        if (c_time > a_time) c_time = a_time;
-        if (c_time > m_time) c_time = m_time;
-
-        /* If we sucessfully retrieved the create time... */
-        if (ret) {
-                /* ... then set the new attributes */
-                ret = cli_setattrE(&srv->cli, fd, c_time, a_time, m_time);
-        }
-
-        cli_close(&srv->cli, fd);
-
-        return ret;
+        return 0;
 }
 
 
@@ -4334,23 +4422,15 @@ int smbc_setxattr_ctx(SMBCCTX *context,
                         dos_attr_parse(context, dad, srv, namevalue);
 
                         /* Set the new DOS attributes */
-#if 0                           /* not yet implemented */
-                        if (! cli_setpathinfo(&srv->cli, path,
-                                              dad->c_time,
-                                              dad->a_time,
-                                              dad->m_time,
-                                              dad->mode)) {
-                                if (!cli_setatr(&srv->cli, path,
-                                                dad->mode, dad->m_time)) {
-                                        errno = smbc_errno(context, &srv->cli);
-                                }
+                        if (! smbc_setatr(context, srv, path,
+                                          dad->c_time,
+                                          dad->a_time,
+                                          dad->m_time,
+                                          dad->mode)) {
+
+                                /* cause failure if NT failed too */
+                                dad = NULL; 
                         }
-#else
-                        if (!cli_setatr(&srv->cli, path,
-                                        dad->mode, dad->m_time)) {
-                                errno = smbc_errno(context, &srv->cli);
-                        }
-#endif
                 }
 
                 /* we only fail if both NT and DOS sets failed */
@@ -4472,28 +4552,11 @@ int smbc_setxattr_ctx(SMBCCTX *context,
                                 dos_attr_parse(context, dad, srv, namevalue);
 
                                 /* Set the new DOS attributes */
-#if 0                           /* not yet implemented */
-                                ret2 = cli_setpathinfo(&srv->cli, path,
-                                                       dad->c_time,
-                                                       dad->a_time,
-                                                       dad->m_time,
-                                                       dad->mode);
-                                if (! ret2) {
-                                        ret2 = cli_setatr(&srv->cli, path,
-                                                          dad->mode,
-                                                          dad->m_time);
-                                        if (! ret2) {
-                                                errno = smbc_errno(context,
-                                                                   &srv->cli);
-                                        }
-                                }
-#else
-                                ret2 = cli_setatr(&srv->cli, path,
-                                                  dad->mode, dad->m_time);
-                                if (! ret2) {
-                                        errno = smbc_errno(context, &srv->cli);
-                                }
-#endif
+                                ret2 = smbc_setatr(context, srv, path,
+                                                   dad->c_time,
+                                                   dad->a_time,
+                                                   dad->m_time,
+                                                   dad->mode);
 
                                 /* ret2 has True (success) / False (failure) */
                                 if (ret2) {