[patch 1/3] add protocol extension to ATTR message

Miklos Szeredi miklos at szeredi.hu
Fri Feb 13 08:19:03 EST 2009


This patch adds all the missing commonly used UNIX attributes: st_dev,
st_ino, st_nlink, st_rdev, st_blocks, st_blksize, st_ctime.  In
addition it extends st_atime and st_mtime to 64bits, and adds
nanosecond resolution to all three timestamps.

This is implemented as an extension to the ATTR message.  This patch
alone is sufficient for SSHFS to be able to use these attributes.  The
following two patches optimize the bandwidth use for this extension.

Index: ssh/sftp-common.c
===================================================================
--- ssh.orig/sftp-common.c	2009-02-12 14:11:15.000000000 +0100
+++ ssh/sftp-common.c	2009-02-12 14:11:31.000000000 +0100
@@ -47,12 +47,23 @@ void
 attrib_clear(Attrib *a)
 {
 	a->flags = 0;
+	a->ext_flags = 0;
+	a->dev = 0;
+	a->ino = 0;
+	a->rdev = 0;
 	a->size = 0;
+	a->blocks = 0;
+	a->blksize = 0;
 	a->uid = 0;
 	a->gid = 0;
 	a->perm = 0;
+	a->nlink = 0;
 	a->atime = 0;
 	a->mtime = 0;
+	a->ctime = 0;
+	a->atimensec = 0;
+	a->mtimensec = 0;
+	a->ctimensec = 0;
 }
 
 /* Convert from struct stat to filexfer attribs */
@@ -61,6 +72,7 @@ stat_to_attrib(const struct stat *st, At
 {
 	attrib_clear(a);
 	a->flags = 0;
+	a->ext_flags = 0;
 	a->flags |= SSH2_FILEXFER_ATTR_SIZE;
 	a->size = st->st_size;
 	a->flags |= SSH2_FILEXFER_ATTR_UIDGID;
@@ -69,8 +81,32 @@ stat_to_attrib(const struct stat *st, At
 	a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
 	a->perm = st->st_mode;
 	a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME;
+	a->ext_flags |= SSH2_FXE_EXTATTR_ATIME;
+	a->ext_flags |= SSH2_FXE_EXTATTR_MTIME;
 	a->atime = st->st_atime;
 	a->mtime = st->st_mtime;
+	a->ext_flags |= SSH2_FXE_EXTATTR_CTIME;
+	a->ctime = st->st_ctime;
+	a->ext_flags |= SSH2_FXE_EXTATTR_ATIMENSEC;
+	a->atimensec = st->st_atimensec;
+	a->ext_flags |= SSH2_FXE_EXTATTR_MTIMENSEC;
+	a->mtimensec = st->st_mtimensec;
+	a->ext_flags |= SSH2_FXE_EXTATTR_CTIMENSEC;
+	a->ctimensec = st->st_ctimensec;
+	a->ext_flags |= SSH2_FXE_EXTATTR_DEV;
+	a->dev = st->st_dev;
+	a->ext_flags |= SSH2_FXE_EXTATTR_INO;
+	a->ino = st->st_ino;
+	a->ext_flags |= SSH2_FXE_EXTATTR_NLINK;
+	a->nlink = st->st_nlink;
+	if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
+		a->ext_flags |= SSH2_FXE_EXTATTR_RDEV;
+		a->rdev = st->st_rdev;
+	}
+	a->ext_flags |= SSH2_FXE_EXTATTR_BLKSIZE;
+	a->blksize = st->st_blksize;
+	a->ext_flags |= SSH2_FXE_EXTATTR_BLOCKS;
+	a->blocks = st->st_blocks;
 }
 
 /* Convert from filexfer attribs to struct stat */
@@ -87,10 +123,62 @@ attrib_to_stat(const Attrib *a, struct s
 	}
 	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
 		st->st_mode = a->perm;
-	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
+	if ((a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) ||
+	    (a->ext_flags & SSH2_FXE_EXTATTR_ATIME))
 		st->st_atime = a->atime;
+	if ((a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) ||
+	    (a->ext_flags & SSH2_FXE_EXTATTR_MTIME))
 		st->st_mtime = a->mtime;
-	}
+	if (a->ext_flags & SSH2_FXE_EXTATTR_CTIME)
+		st->st_ctime = a->ctime;
+	if (a->ext_flags & SSH2_FXE_EXTATTR_ATIMENSEC)
+		st->st_atimensec = a->atimensec;
+	if (a->ext_flags & SSH2_FXE_EXTATTR_MTIMENSEC)
+		st->st_mtimensec = a->mtimensec;
+	if (a->ext_flags & SSH2_FXE_EXTATTR_CTIMENSEC)
+		st->st_ctimensec = a->ctimensec;
+	if (a->ext_flags & SSH2_FXE_EXTATTR_DEV)
+		st->st_dev = a->dev;
+	if (a->ext_flags & SSH2_FXE_EXTATTR_INO)
+		st->st_ino = a->ino;
+	if (a->ext_flags & SSH2_FXE_EXTATTR_NLINK)
+		st->st_nlink = a->nlink;
+	if (a->ext_flags & SSH2_FXE_EXTATTR_RDEV)
+		st->st_rdev = a->rdev;
+	if (a->ext_flags & SSH2_FXE_EXTATTR_BLKSIZE)
+		st->st_blksize = a->blksize;
+	if (a->ext_flags & SSH2_FXE_EXTATTR_BLOCKS)
+		st->st_blocks = a->blocks;
+}
+
+static void
+decode_extra_attrib(Buffer *b, Attrib *a)
+{
+	a->ext_flags = buffer_get_int(b);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_DEV)
+		a->dev = buffer_get_int64(b);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_INO)
+		a->ino = buffer_get_int64(b);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_NLINK)
+		a->nlink = buffer_get_int(b);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_RDEV)
+		a->rdev = buffer_get_int64(b);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_BLKSIZE)
+		a->blksize = buffer_get_int(b);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_BLOCKS)
+		a->blocks = buffer_get_int64(b);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_ATIME)
+		a->atime = buffer_get_int64(b);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_ATIMENSEC)
+		a->atimensec = buffer_get_int(b);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_MTIME)
+		a->mtime = buffer_get_int64(b);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_MTIMENSEC)
+		a->mtimensec = buffer_get_int(b);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_CTIME)
+		a->ctime = buffer_get_int64(b);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_CTIMENSEC)
+		a->ctimensec = buffer_get_int(b);
 }
 
 /* Decode attributes in buffer */
@@ -115,26 +203,70 @@ decode_attrib(Buffer *b)
 	}
 	/* vendor-specific extensions */
 	if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) {
-		char *type, *data;
+		char *type;
+		void *data;
 		int i, count;
+		u_int datalen;
 
 		count = buffer_get_int(b);
 		for (i = 0; i < count; i++) {
 			type = buffer_get_string(b, NULL);
-			data = buffer_get_string(b, NULL);
+			data = buffer_get_string_ptr(b, &datalen);
 			debug3("Got file attribute \"%s\"", type);
+			if (strcmp(type, "extattr at openssh.com") == 0) {
+				Buffer ext;
+
+				buffer_init(&ext);
+				buffer_append(&ext, data, datalen);
+				decode_extra_attrib(&ext, &a);
+				buffer_free(&ext);
+			}
 			xfree(type);
-			xfree(data);
 		}
 	}
 	return &a;
 }
 
+static void
+encode_extra_attrib(Buffer *b, const Attrib *a)
+{
+	buffer_put_int(b, a->ext_flags);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_DEV)
+		buffer_put_int64(b, a->dev);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_INO)
+		buffer_put_int64(b, a->ino);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_NLINK)
+		buffer_put_int(b, a->nlink);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_RDEV)
+		buffer_put_int64(b, a->rdev);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_BLKSIZE)
+		buffer_put_int(b, a->blksize);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_BLOCKS)
+		buffer_put_int64(b, a->blocks);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_ATIME)
+		buffer_put_int64(b, a->atime);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_ATIMENSEC)
+		buffer_put_int(b, a->atimensec);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_MTIME)
+		buffer_put_int64(b, a->mtime);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_MTIMENSEC)
+		buffer_put_int(b, a->mtimensec);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_CTIME)
+		buffer_put_int64(b, a->ctime);
+	if (a->ext_flags & SSH2_FXE_EXTATTR_CTIMENSEC)
+		buffer_put_int(b, a->ctimensec);
+}
+
 /* Encode attributes to buffer */
 void
 encode_attrib(Buffer *b, const Attrib *a)
 {
-	buffer_put_int(b, a->flags);
+	int flags = a->flags;
+
+	if (a->ext_flags != 0)
+		flags |= SSH2_FILEXFER_ATTR_EXTENDED;
+
+	buffer_put_int(b, flags);
 	if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
 		buffer_put_int64(b, a->size);
 	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
@@ -147,6 +279,19 @@ encode_attrib(Buffer *b, const Attrib *a
 		buffer_put_int(b, a->atime);
 		buffer_put_int(b, a->mtime);
 	}
+
+	if (flags & SSH2_FILEXFER_ATTR_EXTENDED) {
+		u_int ext_count = 1;
+		Buffer ext;
+
+		buffer_put_int(b, ext_count);
+		buffer_put_cstring(b, "extattr at openssh.com");
+
+		buffer_init(&ext);
+		encode_extra_attrib(&ext, a);
+		buffer_put_string(b, buffer_ptr(&ext), buffer_len(&ext));
+		buffer_free(&ext);
+	}
 }
 
 /* Convert from SSH2_FX_ status to text error message */
Index: ssh/sftp-common.h
===================================================================
--- ssh.orig/sftp-common.h	2009-02-12 14:11:15.000000000 +0100
+++ ssh/sftp-common.h	2009-02-12 14:11:31.000000000 +0100
@@ -33,12 +33,23 @@ typedef struct Attrib Attrib;
 /* File attributes */
 struct Attrib {
 	u_int32_t	flags;
+	u_int32_t	ext_flags;
+	u_int64_t	dev;
+	u_int64_t	ino;
+	u_int64_t	rdev;
 	u_int64_t	size;
+	u_int64_t	blocks;
+	u_int32_t	blksize;
 	u_int32_t	uid;
 	u_int32_t	gid;
 	u_int32_t	perm;
-	u_int32_t	atime;
-	u_int32_t	mtime;
+	u_int32_t	nlink;
+	u_int64_t	atime;
+	u_int64_t	mtime;
+	u_int64_t	ctime;
+	u_int32_t	atimensec;
+	u_int32_t	mtimensec;
+	u_int32_t	ctimensec;
 };
 
 void	 attrib_clear(Attrib *);
Index: ssh/sftp.h
===================================================================
--- ssh.orig/sftp.h	2009-02-12 14:11:15.000000000 +0100
+++ ssh/sftp.h	2009-02-12 14:11:31.000000000 +0100
@@ -83,6 +83,20 @@
 #define SSH2_FXE_STATVFS_ST_RDONLY	0x00000001
 #define SSH2_FXE_STATVFS_ST_NOSUID	0x00000002
 
+/* extattr at openssh.com extra attribute flags */
+#define SSH2_FXE_EXTATTR_DEV		0x00000001
+#define SSH2_FXE_EXTATTR_INO		0x00000002
+#define SSH2_FXE_EXTATTR_NLINK		0x00000004
+#define SSH2_FXE_EXTATTR_RDEV		0x00000008
+#define SSH2_FXE_EXTATTR_BLKSIZE	0x00000010
+#define SSH2_FXE_EXTATTR_BLOCKS		0x00000020
+#define SSH2_FXE_EXTATTR_ATIME		0x00000040
+#define SSH2_FXE_EXTATTR_ATIMENSEC	0x00000080
+#define SSH2_FXE_EXTATTR_MTIME		0x00000100
+#define SSH2_FXE_EXTATTR_MTIMENSEC	0x00000200
+#define SSH2_FXE_EXTATTR_CTIME		0x00000400
+#define SSH2_FXE_EXTATTR_CTIMENSEC	0x00000800
+
 /* status messages */
 #define SSH2_FX_OK			0
 #define SSH2_FX_EOF			1
Index: ssh/sftp-client.c
===================================================================
--- ssh.orig/sftp-client.c	2009-02-12 14:11:30.000000000 +0100
+++ ssh/sftp-client.c	2009-02-12 14:11:37.000000000 +0100
@@ -1201,6 +1201,7 @@ do_upload(struct sftp_conn *conn, char *
 	}
 	stat_to_attrib(&sb, &a);
 
+	a.ext_flags = 0;
 	a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
 	a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
 	a.perm &= 0777;



More information about the openssh-unix-dev mailing list