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;