commit bb9e3216bf32032a08a6f48799991f05203fa54b
Author: Anthony Liguori <aliguori@us.ibm.com>
Date:   Thu Apr 29 17:44:49 2010 +0530

    virtio-9p: Add stat and mode related helper functions.
    
    Add helpers to obtain file stat and mode details.
    
    Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
    Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
    Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>

diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c
index 4467aea..b37cdf2 100644
--- a/hw/virtio-9p.c
+++ b/hw/virtio-9p.c
@@ -580,6 +580,188 @@ static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
     free_pdu(s, pdu);
 }
 
+static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
+{
+    mode_t ret;
+
+    ret = mode & 0777;
+    if (mode & P9_STAT_MODE_DIR) {
+        ret |= S_IFDIR;
+    }
+
+    if (dotu) {
+        if (mode & P9_STAT_MODE_SYMLINK) {
+            ret |= S_IFLNK;
+        }
+        if (mode & P9_STAT_MODE_SOCKET) {
+            ret |= S_IFSOCK;
+        }
+        if (mode & P9_STAT_MODE_NAMED_PIPE) {
+            ret |= S_IFIFO;
+        }
+        if (mode & P9_STAT_MODE_DEVICE) {
+            if (extension && extension->data[0] == 'c') {
+                ret |= S_IFCHR;
+            } else {
+                ret |= S_IFBLK;
+            }
+        }
+    }
+
+    if (!(ret&~0777)) {
+        ret |= S_IFREG;
+    }
+
+    if (mode & P9_STAT_MODE_SETUID) {
+        ret |= S_ISUID;
+    }
+    if (mode & P9_STAT_MODE_SETGID) {
+        ret |= S_ISGID;
+    }
+    if (mode & P9_STAT_MODE_SETVTX) {
+        ret |= S_ISVTX;
+    }
+
+    return ret;
+}
+
+static int donttouch_stat(V9fsStat *stat)
+{
+    if (stat->type == -1 &&
+        stat->dev == -1 &&
+        stat->qid.type == -1 &&
+        stat->qid.version == -1 &&
+        stat->qid.path == -1 &&
+        stat->mode == -1 &&
+        stat->atime == -1 &&
+        stat->mtime == -1 &&
+        stat->length == -1 &&
+        !stat->name.size &&
+        !stat->uid.size &&
+        !stat->gid.size &&
+        !stat->muid.size &&
+        stat->n_uid == -1 &&
+        stat->n_gid == -1 &&
+        stat->n_muid == -1) {
+        return 1;
+    }
+
+    return 0;
+}
+
+static void v9fs_stat_free(V9fsStat *stat)
+{
+    v9fs_string_free(&stat->name);
+    v9fs_string_free(&stat->uid);
+    v9fs_string_free(&stat->gid);
+    v9fs_string_free(&stat->muid);
+    v9fs_string_free(&stat->extension);
+}
+
+static uint32_t stat_to_v9mode(const struct stat *stbuf)
+{
+    uint32_t mode;
+
+    mode = stbuf->st_mode & 0777;
+    if (S_ISDIR(stbuf->st_mode)) {
+        mode |= P9_STAT_MODE_DIR;
+    }
+
+    if (dotu) {
+        if (S_ISLNK(stbuf->st_mode)) {
+            mode |= P9_STAT_MODE_SYMLINK;
+        }
+
+        if (S_ISSOCK(stbuf->st_mode)) {
+            mode |= P9_STAT_MODE_SOCKET;
+        }
+
+        if (S_ISFIFO(stbuf->st_mode)) {
+            mode |= P9_STAT_MODE_NAMED_PIPE;
+        }
+
+        if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) {
+            mode |= P9_STAT_MODE_DEVICE;
+        }
+
+        if (stbuf->st_mode & S_ISUID) {
+            mode |= P9_STAT_MODE_SETUID;
+        }
+
+        if (stbuf->st_mode & S_ISGID) {
+            mode |= P9_STAT_MODE_SETGID;
+        }
+
+        if (stbuf->st_mode & S_ISVTX) {
+            mode |= P9_STAT_MODE_SETVTX;
+        }
+    }
+
+    return mode;
+}
+
+static int stat_to_v9stat(V9fsState *s, V9fsString *name,
+                            const struct stat *stbuf,
+                            V9fsStat *v9stat)
+{
+    int err;
+    const char *str;
+
+    memset(v9stat, 0, sizeof(*v9stat));
+
+    stat_to_qid(stbuf, &v9stat->qid);
+    v9stat->mode = stat_to_v9mode(stbuf);
+    v9stat->atime = stbuf->st_atime;
+    v9stat->mtime = stbuf->st_mtime;
+    v9stat->length = stbuf->st_size;
+
+    v9fs_string_null(&v9stat->uid);
+    v9fs_string_null(&v9stat->gid);
+    v9fs_string_null(&v9stat->muid);
+
+    if (dotu) {
+        v9stat->n_uid = stbuf->st_uid;
+        v9stat->n_gid = stbuf->st_gid;
+        v9stat->n_muid = 0;
+
+        v9fs_string_null(&v9stat->extension);
+
+        if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
+            err = v9fs_do_readlink(s, name, &v9stat->extension);
+            if (err == -1) {
+                err = -errno;
+                return err;
+            }
+            v9stat->extension.data[err] = 0;
+            v9stat->extension.size = err;
+        } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
+            v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
+                    S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
+                    major(stbuf->st_rdev), minor(stbuf->st_rdev));
+        } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) {
+            v9fs_string_sprintf(&v9stat->extension, "%s %u",
+                    "HARDLINKCOUNT", stbuf->st_nlink);
+        }
+    }
+
+    str = strrchr(name->data, '/');
+    if (str) {
+        str += 1;
+    } else {
+        str = name->data;
+    }
+
+    v9fs_string_sprintf(&v9stat->name, "%s", str);
+
+    v9stat->size = 61 +
+        v9fs_string_size(&v9stat->name) +
+        v9fs_string_size(&v9stat->uid) +
+        v9fs_string_size(&v9stat->gid) +
+        v9fs_string_size(&v9stat->muid) +
+        v9fs_string_size(&v9stat->extension);
+    return 0;
+}
+
 static void v9fs_dummy(V9fsState *s, V9fsPDU *pdu)
 {
     /* Note: The following have been added to prevent GCC from complaining
@@ -600,6 +782,10 @@ static void v9fs_dummy(V9fsState *s, V9fsPDU *pdu)
     (void) alloc_fid;
     (void) free_fid;
     (void) fid_to_qid;
+    (void) v9mode_to_mode;
+    (void) donttouch_stat;
+    (void) v9fs_stat_free;
+    (void) stat_to_v9stat;
 }
 
 static void v9fs_version(V9fsState *s, V9fsPDU *pdu)
