Propagate file permissions in both directions in Unix pscp and psftp.
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Thu, 11 Aug 2011 17:59:30 +0000 (17:59 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Thu, 11 Aug 2011 17:59:30 +0000 (17:59 +0000)
I think I have to consider this to be a separate but related change to
the wishlist item 'pscp-filemodes'; that was written before the Unix
port existed, and referred to the ability to configure the permissions
used for files copied from Windows to Unix - which is still not done.

git-svn-id: svn://svn.tartarus.org/sgt/putty@9260 cda61777-01e9-0310-a592-d414129be87e

pscp.c
psftp.c
psftp.h
sftp.c
sftp.h
unix/uxsftp.c
windows/winsftp.c

diff --git a/pscp.c b/pscp.c
index a0910c7..ddf23ec 100644 (file)
--- a/pscp.c
+++ b/pscp.c
@@ -830,12 +830,13 @@ int scp_send_filetimes(unsigned long mtime, unsigned long atime)
     }
 }
 
-int scp_send_filename(char *name, uint64 size, int modes)
+int scp_send_filename(char *name, uint64 size, int permissions)
 {
     if (using_sftp) {
        char *fullname;
        struct sftp_packet *pktin;
        struct sftp_request *req, *rreq;
+        struct fxp_attrs attrs;
 
        if (scp_sftp_targetisdir) {
            fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);
@@ -843,8 +844,12 @@ int scp_send_filename(char *name, uint64 size, int modes)
            fullname = dupstr(scp_sftp_remotepath);
        }
 
+        attrs.flags = 0;
+        PUT_PERMISSIONS(attrs, permissions);
+
        sftp_register(req = fxp_open_send(fullname, SSH_FXF_WRITE |
-                                         SSH_FXF_CREAT | SSH_FXF_TRUNC));
+                                         SSH_FXF_CREAT | SSH_FXF_TRUNC,
+                                          &attrs));
        rreq = sftp_find_request(pktin = sftp_recv());
        assert(rreq == req);
        scp_sftp_filehandle = fxp_open_recv(pktin, rreq);
@@ -864,7 +869,9 @@ int scp_send_filename(char *name, uint64 size, int modes)
        char buf[40];
        char sizestr[40];
        uint64_decimal(size, sizestr);
-       sprintf(buf, "C%04o %s ", modes, sizestr);
+        if (permissions < 0)
+            permissions = 0644;
+       sprintf(buf, "C%04o %s ", (int)(permissions & 07777), sizestr);
        back->send(backhandle, buf, strlen(buf));
        back->send(backhandle, name, strlen(name));
        back->send(backhandle, "\n", 1);
@@ -1144,7 +1151,7 @@ struct scp_sink_action {
     int action;                               /* FILE, DIR, ENDDIR */
     char *buf;                        /* will need freeing after use */
     char *name;                               /* filename or dirname (not ENDDIR) */
-    int mode;                         /* access mode (not ENDDIR) */
+    long permissions;                 /* access permissions (not ENDDIR) */
     uint64 size;                      /* file size (not ENDDIR) */
     int settime;                      /* 1 if atime and mtime are filled */
     unsigned long atime, mtime;               /* access times for the file */
@@ -1370,7 +1377,7 @@ int scp_get_sink_action(struct scp_sink_action *act)
                act->buf = dupstr(stripslashes(fname, 0));
                act->name = act->buf;
                act->size = uint64_make(0,0);     /* duhh, it's a directory */
-               act->mode = 07777 & attrs.permissions;
+               act->permissions = 07777 & attrs.permissions;
                if (scp_sftp_preserve &&
                    (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
                    act->atime = attrs.atime;
@@ -1392,7 +1399,7 @@ int scp_get_sink_action(struct scp_sink_action *act)
                act->size = attrs.size;
            } else
                act->size = uint64_make(ULONG_MAX,ULONG_MAX);   /* no idea */
-           act->mode = 07777 & attrs.permissions;
+           act->permissions = 07777 & attrs.permissions;
            if (scp_sftp_preserve &&
                (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
                act->atime = attrs.atime;
@@ -1474,7 +1481,8 @@ int scp_get_sink_action(struct scp_sink_action *act)
        {
            char sizestr[40];
        
-           if (sscanf(act->buf, "%o %s %n", &act->mode, sizestr, &i) != 2)
+           if (sscanf(act->buf, "%lo %s %n", &act->permissions,
+                       sizestr, &i) != 2)
                bump("Protocol error: Illegal file descriptor format");
            act->size = uint64_from_decimal(sizestr);
            act->name = act->buf + i;
@@ -1489,7 +1497,8 @@ int scp_accept_filexfer(void)
        struct sftp_packet *pktin;
        struct sftp_request *req, *rreq;
 
-       sftp_register(req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ));
+       sftp_register(req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ,
+                                          NULL));
        rreq = sftp_find_request(pktin = sftp_recv());
        assert(rreq == req);
        scp_sftp_filehandle = fxp_open_recv(pktin, rreq);
@@ -1609,6 +1618,7 @@ static void source(char *src)
 {
     uint64 size;
     unsigned long mtime, atime;
+    long permissions;
     char *last;
     RFile *f;
     int attr;
@@ -1656,7 +1666,7 @@ static void source(char *src)
     if (last == src && strchr(src, ':') != NULL)
        last = strchr(src, ':') + 1;
 
-    f = open_existing_file(src, &size, &mtime, &atime);
+    f = open_existing_file(src, &size, &mtime, &atime, &permissions);
     if (f == NULL) {
        run_err("%s: Cannot open file", src);
        return;
@@ -1671,7 +1681,7 @@ static void source(char *src)
        uint64_decimal(size, sizestr);
        tell_user(stderr, "Sending file %s, size=%s", last, sizestr);
     }
-    if (scp_send_filename(last, size, 0644))
+    if (scp_send_filename(last, size, permissions))
        return;
 
     stat_bytes = uint64_make(0,0);
@@ -1888,7 +1898,7 @@ static void sink(char *targ, char *src)
            continue;
        }
 
-       f = open_new_file(destfname);
+       f = open_new_file(destfname, act.permissions);
        if (f == NULL) {
            run_err("%s: Cannot create file", destfname);
            continue;
diff --git a/psftp.c b/psftp.c
index a42b162..5f91822 100644 (file)
--- a/psftp.c
+++ b/psftp.c
@@ -212,6 +212,7 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
     uint64 offset;
     WFile *file;
     int ret, shown_err = FALSE;
+    struct fxp_attrs attrs;
 
     /*
      * In recursive mode, see if we're dealing with a directory.
@@ -219,7 +220,6 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
      * subsequent FXP_OPEN will return a usable error message.)
      */
     if (recurse) {
-       struct fxp_attrs attrs;
        int result;
 
        sftp_register(req = fxp_stat_send(fname));
@@ -383,7 +383,13 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
        }
     }
 
-    sftp_register(req = fxp_open_send(fname, SSH_FXF_READ));
+    sftp_register(req = fxp_stat_send(fname));
+    rreq = sftp_find_request(pktin = sftp_recv());
+    assert(rreq == req);
+    if (!fxp_stat_recv(pktin, rreq, &attrs))
+        attrs.flags = 0;
+
+    sftp_register(req = fxp_open_send(fname, SSH_FXF_READ, NULL));
     rreq = sftp_find_request(pktin = sftp_recv());
     assert(rreq == req);
     fh = fxp_open_recv(pktin, rreq);
@@ -396,7 +402,7 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
     if (restart) {
        file = open_existing_wfile(outfname, NULL);
     } else {
-       file = open_new_file(outfname);
+       file = open_new_file(outfname, GET_PERMISSIONS(attrs));
     }
 
     if (!file) {
@@ -500,6 +506,8 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
     uint64 offset;
     RFile *file;
     int ret, err, eof;
+    struct fxp_attrs attrs;
+    long permissions;
 
     /*
      * In recursive mode, see if we're dealing with a directory.
@@ -507,7 +515,6 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
      * subsequent fopen will return an error message.)
      */
     if (recurse && file_type(fname) == FILE_TYPE_DIRECTORY) {
-       struct fxp_attrs attrs;
        int result;
        int nnames, namesize;
        char *name, **ournames;
@@ -630,16 +637,19 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
        return 1;
     }
 
-    file = open_existing_file(fname, NULL, NULL, NULL);
+    file = open_existing_file(fname, NULL, NULL, NULL, &permissions);
     if (!file) {
        printf("local: unable to open %s\n", fname);
        return 0;
     }
+    attrs.flags = 0;
+    PUT_PERMISSIONS(attrs, permissions);
     if (restart) {
-       sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE));
+       sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE, &attrs));
     } else {
        sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE |
-                                         SSH_FXF_CREAT | SSH_FXF_TRUNC));
+                                         SSH_FXF_CREAT | SSH_FXF_TRUNC,
+                                          &attrs));
     }
     rreq = sftp_find_request(pktin = sftp_recv());
     assert(rreq == req);
diff --git a/psftp.h b/psftp.h
index 8d9391c..e6ad00f 100644 (file)
--- a/psftp.h
+++ b/psftp.h
@@ -85,15 +85,17 @@ void gui_enable(char *arg);
  */
 typedef struct RFile RFile;
 typedef struct WFile WFile;
-/* Output params size, mtime and atime can all be NULL if desired */
+/* Output params size, perms, mtime and atime can all be NULL if
+ * desired. perms will be -1 if the OS does not support POSIX permissions. */
 RFile *open_existing_file(char *name, uint64 *size,
-                         unsigned long *mtime, unsigned long *atime);
+                         unsigned long *mtime, unsigned long *atime,
+                          long *perms);
 WFile *open_existing_wfile(char *name, uint64 *size);
 /* Returns <0 on error, 0 on eof, or number of bytes read, as usual */
 int read_from_file(RFile *f, void *buffer, int length);
 /* Closes and frees the RFile */
 void close_rfile(RFile *f);
-WFile *open_new_file(char *name);
+WFile *open_new_file(char *name, long perms);
 /* Returns <0 on error, 0 on eof, or number of bytes written, as usual */
 int write_to_file(WFile *f, void *buffer, int length);
 void set_file_times(WFile *f, unsigned long mtime, unsigned long atime);
diff --git a/sftp.c b/sftp.c
index 52f278e..793f72a 100644 (file)
--- a/sftp.c
+++ b/sftp.c
@@ -548,7 +548,8 @@ char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req)
 /*
  * Open a file.
  */
-struct sftp_request *fxp_open_send(char *path, int type)
+struct sftp_request *fxp_open_send(char *path, int type,
+                                   struct fxp_attrs *attrs)
 {
     struct sftp_request *req = sftp_alloc_request();
     struct sftp_packet *pktout;
@@ -557,7 +558,10 @@ struct sftp_request *fxp_open_send(char *path, int type)
     sftp_pkt_adduint32(pktout, req->id);
     sftp_pkt_addstring(pktout, path);
     sftp_pkt_adduint32(pktout, type);
-    sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
+    if (attrs)
+        sftp_pkt_addattrs(pktout, *attrs);
+    else
+        sftp_pkt_adduint32(pktout, 0); /* empty ATTRS structure */
     sftp_send(pktout);
 
     return req;
diff --git a/sftp.h b/sftp.h
index 031aea2..c3167a5 100644 (file)
--- a/sftp.h
+++ b/sftp.h
@@ -82,6 +82,19 @@ struct fxp_attrs {
     unsigned long mtime;
 };
 
+/*
+ * Copy between the possibly-unused permissions field in an fxp_attrs
+ * and a possibly-negative integer containing the same permissions.
+ */
+#define PUT_PERMISSIONS(attrs, perms)                   \
+    ((perms) >= 0 ?                                     \
+     ((attrs).flags |= SSH_FILEXFER_ATTR_PERMISSIONS,   \
+      (attrs).permissions = (perms)) :                  \
+     ((attrs).flags &= ~SSH_FILEXFER_ATTR_PERMISSIONS))
+#define GET_PERMISSIONS(attrs)                          \
+    ((attrs).flags & SSH_FILEXFER_ATTR_PERMISSIONS ?    \
+     (attrs).permissions : -1)
+
 struct fxp_handle {
     char *hstring;
     int hlen;
@@ -116,9 +129,11 @@ struct sftp_request *fxp_realpath_send(char *path);
 char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req);
 
 /*
- * Open a file.
+ * Open a file. 'attrs' contains attributes to be applied to the file
+ * if it's being created.
  */
-struct sftp_request *fxp_open_send(char *path, int type);
+struct sftp_request *fxp_open_send(char *path, int type,
+                                   struct fxp_attrs *attrs);
 struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,
                                 struct sftp_request *req);
 
index 3170ecf..651047e 100644 (file)
@@ -125,7 +125,8 @@ struct RFile {
 };
 
 RFile *open_existing_file(char *name, uint64 *size,
-                         unsigned long *mtime, unsigned long *atime)
+                         unsigned long *mtime, unsigned long *atime,
+                          long *perms)
 {
     int fd;
     RFile *ret;
@@ -137,7 +138,7 @@ RFile *open_existing_file(char *name, uint64 *size,
     ret = snew(RFile);
     ret->fd = fd;
 
-    if (size || mtime || atime) {
+    if (size || mtime || atime || perms) {
        struct stat statbuf;
        if (fstat(fd, &statbuf) < 0) {
            fprintf(stderr, "%s: stat: %s\n", name, strerror(errno));
@@ -153,6 +154,9 @@ RFile *open_existing_file(char *name, uint64 *size,
 
        if (atime)
            *atime = statbuf.st_atime;
+
+       if (perms)
+           *perms = statbuf.st_mode;
     }
 
     return ret;
@@ -174,12 +178,13 @@ struct WFile {
     char *name;
 };
 
-WFile *open_new_file(char *name)
+WFile *open_new_file(char *name, long perms)
 {
     int fd;
     WFile *ret;
 
-    fd = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666);
+    fd = open(name, O_CREAT | O_TRUNC | O_WRONLY,
+              (mode_t)(perms ? perms : 0666));
     if (fd < 0)
        return NULL;
 
index de84997..e6b55c1 100644 (file)
@@ -88,7 +88,8 @@ struct RFile {
 };
 
 RFile *open_existing_file(char *name, uint64 *size,
-                         unsigned long *mtime, unsigned long *atime)
+                         unsigned long *mtime, unsigned long *atime,
+                          long *perms)
 {
     HANDLE h;
     RFile *ret;
@@ -113,6 +114,9 @@ RFile *open_existing_file(char *name, uint64 *size,
            TIME_WIN_TO_POSIX(wrtime, *mtime);
     }
 
+    if (perms)
+        *perms = -1;
+
     return ret;
 }
 
@@ -137,7 +141,7 @@ struct WFile {
     HANDLE h;
 };
 
-WFile *open_new_file(char *name)
+WFile *open_new_file(char *name, long perms)
 {
     HANDLE h;
     WFile *ret;