[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