[patch 2/3] add attribute configuration message

Miklos Szeredi miklos at szeredi.hu
Fri Feb 13 08:25:35 EST 2009


This patch adds a new extended request with which the client can
request which attributes it wants to receive.  By default only the
basic attributes are transferred, but the client can request that any
subset of the basic or extra attributes to be sent.  Attributes can be
selected separately for STAT and READDIR requests.

Also there's a flag to turn off the generation of long names in
READDIR requests.

The server also sends the attributes it can actually support in the
attribute configuration reply so the client may make decisions based
on the server's capabilities.

Index: ssh/sftp-server.c
===================================================================
--- ssh.orig/sftp-server.c	2009-02-12 14:11:30.000000000 +0100
+++ ssh/sftp-server.c	2009-02-12 14:37:39.000000000 +0100
@@ -61,6 +61,45 @@ Buffer oqueue;
 /* Version of client */
 int version;
 
+/* Attributes which this implementation can send */
+#define SUPPORTED_ATTR (			\
+	SSH2_FILEXFER_ATTR_SIZE |		\
+	SSH2_FILEXFER_ATTR_UIDGID |		\
+	SSH2_FILEXFER_ATTR_PERMISSIONS |	\
+	SSH2_FILEXFER_ATTR_ACMODTIME		\
+	)
+
+/* Extra attributes which this implementation can send */
+#define SUPPORTED_EXT_ATTR (			\
+	SSH2_FXE_EXTATTR_DEV |			\
+	SSH2_FXE_EXTATTR_INO |			\
+	SSH2_FXE_EXTATTR_NLINK |		\
+	SSH2_FXE_EXTATTR_RDEV |			\
+	SSH2_FXE_EXTATTR_BLKSIZE |		\
+	SSH2_FXE_EXTATTR_BLOCKS |		\
+	SSH2_FXE_EXTATTR_ATIME |		\
+	SSH2_FXE_EXTATTR_ATIMENSEC |		\
+	SSH2_FXE_EXTATTR_MTIME |		\
+	SSH2_FXE_EXTATTR_MTIMENSEC |		\
+	SSH2_FXE_EXTATTR_CTIME |		\
+	SSH2_FXE_EXTATTR_CTIMENSEC		\
+	)
+
+/* Attributes to send in the stat reply */
+u_int stat_attr_mask = SUPPORTED_ATTR;
+
+/* Extra attributes to send in the stat reply */
+u_int stat_ext_attr_mask = 0;
+
+/* Attributes to send in the readdir reply */
+u_int dir_attr_mask = SUPPORTED_ATTR;
+
+/* Extra attributes to send in the readdir reply */
+u_int dir_ext_attr_mask = 0;
+
+/* If false, send empty long name in the readdir reply */
+int dir_send_long_name = 1;
+
 /* portable attributes, etc. */
 
 typedef struct Stat Stat;
@@ -502,6 +541,28 @@ send_statvfs(u_int32_t id, struct statvf
 	buffer_free(&msg);
 }
 
+static void
+send_attrconfig_reply(u_int32_t id)
+{
+	Buffer msg;
+	int flags = 0;
+
+	/* Send the actual value of the masks */
+	if (!dir_send_long_name)
+		flags |= SSH2_FXE_ATTRCONFIG_NOLONGNAME;
+
+	buffer_init(&msg);
+	buffer_put_char(&msg, SSH2_FXP_EXTENDED_REPLY);
+	buffer_put_int(&msg, id);
+	buffer_put_int(&msg, flags);
+	buffer_put_int(&msg, stat_attr_mask);
+	buffer_put_int(&msg, stat_ext_attr_mask);
+	buffer_put_int(&msg, dir_attr_mask);
+	buffer_put_int(&msg, dir_ext_attr_mask);
+	send_msg(&msg);
+	buffer_free(&msg);
+}
+
 /* parse incoming */
 
 static void
@@ -526,6 +587,9 @@ process_init(void)
 	/* link extension */
 	buffer_put_cstring(&msg, "link at openssh.com");
 	buffer_put_cstring(&msg, "1"); /* version */
+	/* attrconfig extension */
+	buffer_put_cstring(&msg, "attrconfig at openssh.com");
+	buffer_put_cstring(&msg, "1"); /* version */
 	send_msg(&msg);
 	buffer_free(&msg);
 }
@@ -677,6 +741,8 @@ process_do_stat(int do_lstat)
 		status = errno_to_portable(errno);
 	} else {
 		stat_to_attrib(&st, &a);
+		a.flags &= stat_attr_mask;
+		a.ext_flags &= stat_ext_attr_mask;
 		send_attrib(id, &a);
 		status = SSH2_FX_OK;
 	}
@@ -716,6 +782,8 @@ process_fstat(void)
 			status = errno_to_portable(errno);
 		} else {
 			stat_to_attrib(&st, &a);
+			a.flags &= stat_attr_mask;
+			a.ext_flags &= stat_ext_attr_mask;
 			send_attrib(id, &a);
 			status = SSH2_FX_OK;
 		}
@@ -891,6 +959,8 @@ process_readdir(void)
 
 		stats = xcalloc(nstats, sizeof(Stat));
 		while ((dp = readdir(dirp)) != NULL) {
+			char *long_name;
+
 			if (count >= nstats) {
 				nstats *= 2;
 				stats = xrealloc(stats, nstats, sizeof(Stat));
@@ -901,8 +971,16 @@ process_readdir(void)
 			if (lstat(pathname, &st) < 0)
 				continue;
 			stat_to_attrib(&st, &(stats[count].attrib));
+
+			if (dir_send_long_name)
+				long_name = ls_file(dp->d_name, &st, 0);
+			else
+				long_name = xstrdup("");
+
+			stats[count].attrib.flags &= dir_attr_mask;
+			stats[count].attrib.ext_flags &= dir_ext_attr_mask;
 			stats[count].name = xstrdup(dp->d_name);
-			stats[count].long_name = ls_file(dp->d_name, &st, 0);
+			stats[count].long_name = long_name;
 			count++;
 			/* send up to 100 entries in one message */
 			/* XXX check packet size instead */
@@ -1173,6 +1251,33 @@ process_extended_link(u_int32_t id)
 }
 
 static void
+process_extended_attrconfig(u_int32_t id)
+{
+	u_int flags;
+	u_int mask;
+
+	flags = get_int();
+	if (flags & SSH2_FXE_ATTRCONFIG_NOLONGNAME)
+		dir_send_long_name = 0;
+	else
+		dir_send_long_name = 1;
+
+	mask = get_int();
+	stat_attr_mask = mask & SUPPORTED_ATTR;
+	mask = get_int();
+	stat_ext_attr_mask = mask & SUPPORTED_EXT_ATTR;
+	mask = get_int();
+	dir_attr_mask = mask & SUPPORTED_ATTR;
+	mask = get_int();
+	dir_ext_attr_mask = mask & SUPPORTED_EXT_ATTR;
+
+	debug3("request %u: attrconfig", id);
+
+	send_attrconfig_reply(id);
+}
+
+
+static void
 process_extended(void)
 {
 	u_int32_t id;
@@ -1188,6 +1293,8 @@ process_extended(void)
 		process_extended_fstatvfs(id);
 	else if (strcmp(request, "link at openssh.com") == 0)
 		process_extended_link(id);
+	else if (strcmp(request, "attrconfig at openssh.com") == 0)
+		process_extended_attrconfig(id);
 	else
 		send_status(id, SSH2_FX_OP_UNSUPPORTED);	/* MUST */
 	xfree(request);
Index: ssh/sftp.h
===================================================================
--- ssh.orig/sftp.h	2009-02-12 14:11:31.000000000 +0100
+++ ssh/sftp.h	2009-02-12 14:15:44.000000000 +0100
@@ -97,6 +97,9 @@
 #define SSH2_FXE_EXTATTR_CTIME		0x00000400
 #define SSH2_FXE_EXTATTR_CTIMENSEC	0x00000800
 
+/* attrconfig at openssh.com flags */
+#define SSH2_FXE_ATTRCONFIG_NOLONGNAME	0x00000001
+
 /* status messages */
 #define SSH2_FX_OK			0
 #define SSH2_FX_EOF			1


More information about the openssh-unix-dev mailing list