[PATCH] Add getlink command to sftp

Gary Fernandez GaryF at livevault.com
Wed Nov 6 02:46:16 EST 2002


One of the features missing in sftp is the ability to transfer a symlink.
This patch adds a new command to sftp which performs this transfer.  Note
that it uses messages that already exist in the protocol between client and
server.

This diff is based on OpenSSH 3.4p1.

*** sftp-client.c@@\main\1 Tue Oct  1 17:26:20 2002
--- sftp-client.c Wed Oct 23 15:57:34 2002
***************
*** 666,672 ****
  
  	status = get_status(conn->fd_in, id);
  	if (status != SSH2_FX_OK)
! 		error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
  		    newpath, fx2txt(status));
  
  	return(status);
--- 666,672 ----
  
  	status = get_status(conn->fd_in, id);
  	if (status != SSH2_FX_OK)
! 		error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
  		    newpath, fx2txt(status));
  
  	return(status);
***************
*** 673,679 ****
  }
  
  char *
! do_readlink(struct sftp_conn *conn, char *path)
  {
  	Buffer msg;
  	u_int type, expected_id, count, id;
--- 673,679 ----
  }
  
  char *
! do_readlink(struct sftp_conn *conn, char *path, Attrib *attrib)
  {
  	Buffer msg;
  	u_int type, expected_id, count, id;
***************
*** 712,717 ****
--- 712,720 ----
  
  	debug3("SSH_FXP_READLINK %s -> %s", path, filename);
  
+     if (attrib != NULL)
+        *attrib = *a;
+ 
  	xfree(longname);
  
  	buffer_free(&msg);
***************
*** 719,724 ****
--- 722,774 ----
  	return(filename);
  }
  
+ int
+ do_getlink(struct sftp_conn *conn, char *path)
+ {
+ 	char *dest;
+ 	u_int status = 0;
+ 	int ret;
+ 	struct stat statb;
+ 	char *filename;
+ 	Attrib *a;
+     Attrib attrib;
+ 
+ 	a = do_lstat(conn, path, 0);
+ 	if (a == NULL || !S_ISLNK(a->perm)) {
+ 		if (a != NULL)
+ 			error("%s is not a symlink", path);
+ 		return(-1);
+ 	}
+ 
+ 	dest = do_readlink(conn, path, &attrib);
+ 	if (dest == NULL)
+ 		return(-1);
+ 	filename = strrchr(path, '/');
+ 	if (filename == NULL)
+ 		filename = path;
+ 	else
+ 		filename += 1;
+ 	if (lstat(filename, &statb) == 0) {
+ 		error("Name \"%s\" already exists", filename);
+ 		return(-1);
+ 	}
+ 	else {
+ 		ret = symlink(dest, filename);
+ 		status = (ret == -1) ? errno : 0;
+ 		if (status != 0)
+ 			error("Couldn't create symlink \"%s\": %s",
filename,
+ 			strerror(status));
+ 		if (getuid() == 0 || geteuid() == 0) {
+ 			ret = lchown(filename, attrib.uid, attrib.gid);
+ 			status = (ret == -1) ? errno : 0;
+ 			if (status != 0)
+ 				error("Couldn't set ownership on symlink
\"%s\": %s", filename,
+ 				strerror(status));
+         }
+ 	}
+ 	return(status);
+ }
+ 
  static void
  send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
      char *handle, u_int handle_len)

*** sftp.1@@\main\1 Tue Oct  1 17:26:04 2002
--- sftp.1 Wed Oct  2 07:59:00 2002
***************
*** 185,190 ****
--- 185,199 ----
  .Fl P
  flag is specified, then the file's full permission and access time are
  copied too.
+ .It Xo Ic getlink
+ .Ar remote-path
+ .Xc
+ Retrieve the
+ .Ar remote-path
+ and store it on the local machine. This is used to retrieve a symlink
+ rather than the target of the symlink. Since a local
+ path name is not specified, the symlink is given the same name it has on
the
+ remote machine.
  .It Ic help
  Display help text.
  .It Ic lls Op Ar ls-options Op Ar path

*** sftp-int.c@@\main\1 Tue Oct  1 17:27:02 2002
--- sftp-int.c Wed Oct  2 06:21:10 2002
***************
*** 74,79 ****
--- 74,80 ----
  #define I_SHELL		20
  #define I_SYMLINK	21
  #define I_VERSION	22
+ #define I_GETLINK	23
  
  struct CMD {
  	const char *c;
***************
*** 91,96 ****
--- 92,98 ----
  	{ "exit",	I_QUIT },
  	{ "get",	I_GET },
  	{ "mget",	I_GET },
+ 	{ "getlink",	I_GETLINK },
  	{ "help",	I_HELP },
  	{ "lcd",	I_LCHDIR },
  	{ "lchdir",	I_LCHDIR },
***************
*** 126,131 ****
--- 128,134 ----
  	printf("chown own path                Change owner of file 'path' to
'own'\n");
  	printf("help                          Display this help text\n");
  	printf("get remote-path [local-path]  Download file\n");
+ 	printf("getlink remote-path           Download symlink\n");
  	printf("lls [ls-options [path]]       Display local directory
listing\n");
  	printf("ln oldpath newpath            Symlink remote file\n");
  	printf("lmkdir path                   Create local directory\n");
***************
*** 582,587 ****
--- 585,591 ----
  	case I_CHDIR:
  	case I_LCHDIR:
  	case I_LMKDIR:
+ 	case I_GETLINK:
  		/* Get pathname (mandatory) */
  		if (get_pathname(&cp, path1))
  			return(-1);
***************
*** 682,687 ****
--- 686,695 ----
  	case I_SYMLINK:
  		path2 = make_absolute(path2, *pwd);
  		err = do_symlink(conn, path1, path2);
+ 		break;
+ 	case I_GETLINK:
+ 		path1 = make_absolute(path1, *pwd);
+ 		err = do_getlink(conn, path1);
  		break;
  	case I_RM:
  		path1 = make_absolute(path1, *pwd);
*** sftp-client.h@@\main\1 Tue Oct  1 17:26:26 2002
--- sftp-client.h Wed Oct  2 06:11:40 2002
***************
*** 90,97 ****
  /* Rename 'oldpath' to 'newpath' */
  int do_symlink(struct sftp_conn *, char *, char *);
  
  /* Return target of symlink 'path' - caller must free result */
! char *do_readlink(struct sftp_conn *, char *);
  
  /* XXX: add callbacks to do_download/do_upload so we can do progress meter
*/
  
--- 90,100 ----
  /* Rename 'oldpath' to 'newpath' */
  int do_symlink(struct sftp_conn *, char *, char *);
  
+ /* Download symlink 'path' */
+ int do_getlink(struct sftp_conn *, char *);
+ 
  /* Return target of symlink 'path' - caller must free result */
! char *do_readlink(struct sftp_conn *, char *, Attrib *);
  
  /* XXX: add callbacks to do_download/do_upload so we can do progress meter
*/

*** sftp-server.c@@\main\1 Tue Oct  1 17:27:26 2002
--- sftp-server.c Tue Nov  5 10:07:54 2002
***************
*** 604,610 ****
  			status = errno_to_portable(errno);
  	}
  	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
! 		ret = chown(name, a->uid, a->gid);
  		if (ret == -1)
  			status = errno_to_portable(errno);
  	}
--- 618,624 ----
  			status = errno_to_portable(errno);
  	}
  	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
! 		ret = lchown(name, a->uid, a->gid);
  		if (ret == -1)
  			status = errno_to_portable(errno);
  	}
***************
*** 907,915 ****
  		send_status(id, errno_to_portable(errno));
  	else {
  		Stat s;
! 
  		link[len] = '\0';
  		attrib_clear(&s.attrib);
  		s.name = s.long_name = link;
  		send_names(id, 1, &s);
  	}
--- 941,958 ----
  		send_status(id, errno_to_portable(errno));
  	else {
  		Stat s;
! 		struct stat st;
! 		int status;
  		link[len] = '\0';
  		attrib_clear(&s.attrib);
+ 
+ 		status = lstat(path, &st);
+ 		if (status == 0) {
+ 			stat_to_attrib(&st, &s.attrib);
+ 		}
+ 		else {
+ 			send_status(id, errno_to_portable(errno));
+ 		}
  		s.name = s.long_name = link;
  		send_names(id, 1, &s);
  	}




More information about the openssh-unix-dev mailing list