diff options
author | Jeff Garzik <jeff@garzik.org> | 2006-08-04 10:30:19 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-08-04 10:30:19 -0400 |
commit | edfeb83763d87b31ab8f2359e4c826edb64d6be2 (patch) | |
tree | 355796e8847dbc0d470dc5c2dd62546ff2156300 | |
parent | c8f869f43eda037b0e6c7be24a2fdb5dec4bab1b (diff) | |
download | dbfs-edfeb83763d87b31ab8f2359e4c826edb64d6be2.tar.gz |
Implement listxattr op.
Also, prefer 'void *' to 'char *' when referring to anonymous blobs of RAM.
-rw-r--r-- | dbfs-backend.c | 222 | ||||
-rw-r--r-- | dbfs.c | 30 | ||||
-rw-r--r-- | dbfs.h | 19 |
3 files changed, 254 insertions, 17 deletions
diff --git a/dbfs-backend.c b/dbfs-backend.c index 14eeb2b..0f1b2e9 100644 --- a/dbfs-backend.c +++ b/dbfs-backend.c @@ -581,6 +581,192 @@ err_out: return rc; } +static int dbfs_xattr_list_read(DBT *key, DBT *val, char *keystr, guint64 ino) +{ + snprintf(keystr, 32, "/xattr/%Lu", (unsigned long long) ino); + + memset(key, 0, sizeof(*key)); + key->data = keystr; + key->size = strlen(keystr); + + memset(val, 0, sizeof(*val)); + val->flags = DB_DBT_MALLOC; + + return gfs->meta->get(gfs->meta, NULL, key, val, 0); +} + +static int dbfs_xattr_list_add(guint64 ino, const char *name) +{ + struct dbfs_xlist *ent; + char keystr[32]; + DBT key, val; + size_t alloc_len; + size_t name_len = strlen(name); + int rc; + + /* get list from db */ + rc = dbfs_xattr_list_read(&key, &val, keystr, ino); + if (rc && (rc != DB_NOTFOUND)) + return -EIO; + + alloc_len = dbfs_xlist_next(name_len); + + /* if list not found, create new one */ + if (rc == DB_NOTFOUND) { + ent = malloc(alloc_len); + if (!ent) + return -ENOMEM; + + val.data = ent; + val.size = alloc_len; + } + + /* otherwise, append to existing list */ + else { + void *mem; + size_t old_size; + + alloc_len += val.size; + mem = realloc(val.data, alloc_len); + if (!mem) { + rc = -ENOMEM; + goto out; + } + + old_size = val.size; + val.data = mem; + val.size = alloc_len; + + mem += old_size; + ent = mem; + } + + /* fill in list entry at tail of list */ + ent->namelen = GUINT32_TO_LE(name_len); + memcpy(ent->name, name, name_len); + + /* store new list in db */ + rc = gfs->meta->put(gfs->meta, NULL, &key, &val, 0) ? -EIO : 0; + +out: + free(val.data); + return rc; +} + +static int dbfs_xattr_list_del(guint64 ino, const char *name) +{ + size_t name_len = strlen(name); + struct dbfs_xlist *ent; + char keystr[32]; + DBT key, val; + int rc; + long bytes; + void *mem; + size_t ssize = 0; + + /* get list from db */ + rc = dbfs_xattr_list_read(&key, &val, keystr, ino); + if (rc == DB_NOTFOUND) + return -ENOENT; + if (rc) + return -EIO; + + /* find entry in list */ + mem = val.data; + bytes = val.size; + while (bytes > 0) { + ent = mem; + ssize = dbfs_xlist_next(GUINT32_FROM_LE(ent->namelen)); + if (ssize > bytes) { /* data corrupt */ + rc = -EIO; + goto out; + } + + if (!memcmp(ent->name, name, name_len)) + break; + + bytes -= ssize; + } + + /* if not found, exit */ + if (bytes <= 0) { + rc = -ENOENT; + goto out; + } + + /* swallow entry */ + memmove(mem, mem + ssize, bytes - ssize); + val.size -= ssize; + + /* store new list in db */ + rc = gfs->meta->put(gfs->meta, NULL, &key, &val, 0) ? -EIO : 0; + +out: + free(val.data); + return rc; +} + +int dbfs_xattr_list(guint64 ino, void **buf_out, size_t *buflen_out) +{ + struct dbfs_xlist *ent; + char keystr[32]; + DBT key, val; + void *mem, *name_list; + size_t name_list_len, ssize, name_len; + long bytes; + char null = 0; + int rc; + + *buf_out = NULL; + *buflen_out = 0; + + /* get list from db */ + rc = dbfs_xattr_list_read(&key, &val, keystr, ino); + if (rc == DB_NOTFOUND) + return 0; + if (rc) + return -EIO; + if (val.size == 0) + return 0; + + /* allocate output buffer */ + name_list = malloc(val.size); + if (!name_list) { + rc = -ENOMEM; + goto out; + } + name_list_len = 0; + + /* fill output buffer */ + mem = val.data; + bytes = val.size; + while (bytes > 0) { + ent = mem; + name_len = GUINT32_FROM_LE(ent->namelen); + ssize = dbfs_xlist_next(name_len); + + if (ssize > bytes) { /* data corrupt */ + rc = -EIO; + goto out; + } + + memcpy(name_list + name_list_len, ent->name, name_len); + name_list_len += name_len; + + memcpy(name_list + name_list_len, &null, 1); + name_list_len++; + + bytes -= ssize; + } + + *buf_out = name_list; + *buflen_out = name_list_len; + +out: + free(val.data); + return rc; +} + static int dbfs_xattr_read(guint64 ino, const char *name, DBT *val) { char key_str[DBFS_XATTR_NAME_LEN + 32]; @@ -604,7 +790,7 @@ static int dbfs_xattr_read(guint64 ino, const char *name, DBT *val) } static int dbfs_xattr_write(guint64 ino, const char *name, - const char *buf, size_t buflen) + const void *buf, size_t buflen) { char key_str[DBFS_XATTR_NAME_LEN + 32]; DBT key, val; @@ -624,7 +810,7 @@ static int dbfs_xattr_write(guint64 ino, const char *name, } int dbfs_xattr_get(guint64 ino_n, const char *name, - char **buf_out, size_t *buflen_out) + void **buf_out, size_t *buflen_out) { int rc; DBT val; @@ -639,13 +825,19 @@ int dbfs_xattr_get(guint64 ino_n, const char *name, return 0; } -int dbfs_xattr_set(guint64 ino_n, const char *name, const char *buf, +int dbfs_xattr_set(guint64 ino_n, const char *name, const void *buf, size_t buflen, int flags) { - char *current = NULL; + void *current = NULL; size_t current_len = 0; + size_t name_len = strlen(name); int rc, exists; + if ((!name) || (!*name) || (name_len > DBFS_XATTR_NAME_LEN) || + (!g_utf8_validate(name, name_len, NULL)) || + (buflen > DBFS_XATTR_MAX_LEN)) + return -EINVAL; + rc = dbfs_xattr_get(ino_n, name, ¤t, ¤t_len); if (rc && (rc != -EINVAL)) return rc; @@ -657,15 +849,21 @@ int dbfs_xattr_set(guint64 ino_n, const char *name, const char *buf, return -EEXIST; if (!exists && (flags & XATTR_REPLACE)) return -ENOATTR; - if (buflen > DBFS_XATTR_MAX_LEN) - return -ENOSPC; /* TODO: return value sane? */ - /* FIXME: update list of xattrs for this inode */ + rc = dbfs_xattr_write(ino_n, name, buf, buflen); + if (rc) + return rc; - return dbfs_xattr_write(ino_n, name, buf, buflen); + rc = dbfs_xattr_list_add(ino_n, name); + if (rc) { + dbfs_xattr_remove(ino_n, name, FALSE); + return rc; + } + + return 0; } -int dbfs_xattr_remove(guint64 ino_n, const char *name) +int dbfs_xattr_remove(guint64 ino_n, const char *name, gboolean update_list) { char key_str[DBFS_XATTR_NAME_LEN + 32]; DBT key; @@ -678,7 +876,11 @@ int dbfs_xattr_remove(guint64 ino_n, const char *name) key.data = key_str; key.size = strlen(key_str); - /* FIXME: update list of xattrs for this inode */ + if (update_list) { + rc = dbfs_xattr_list_del(ino_n, name); + if (rc) + return rc; + } rc = gfs->meta->del(gfs->meta, NULL, &key, 0); if (rc == DB_NOTFOUND) @@ -362,7 +362,7 @@ static void dirbuf_add(struct dirbuf *b, const char *name, fuse_ino_t ino) #endif /* stock function copied from FUSE template */ -static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize, +static int reply_buf_limited(fuse_req_t req, const void *buf, size_t bufsize, off_t off, size_t maxsize) { if (off < bufsize) @@ -413,7 +413,7 @@ static void dbfs_op_setxattr(fuse_req_t req, fuse_ino_t ino, static void dbfs_op_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size) { - char *buf = NULL; + void *buf = NULL; size_t buflen = 0; int rc; @@ -437,10 +437,32 @@ err_out: fuse_reply_err(req, -rc); } +static void dbfs_op_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) +{ + int rc; + void *buf; + size_t buflen; + + rc = dbfs_xattr_list(ino, &buf, &buflen); + if (rc < 0) { + fuse_reply_err(req, -rc); + return; + } + + if (size == 0) + fuse_reply_xattr(req, buflen); + else if (size < buflen) + fuse_reply_err(req, ERANGE); + else + fuse_reply_buf(req, buf, buflen); + + free(buf); +} + static void dbfs_op_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) { - int rc = dbfs_xattr_remove(ino, name); + int rc = dbfs_xattr_remove(ino, name, TRUE); fuse_reply_err(req, -rc); } @@ -472,7 +494,7 @@ static struct fuse_lowlevel_ops dbfs_ops = { .statfs = NULL, .setxattr = dbfs_op_setxattr, .getxattr = dbfs_op_getxattr, - .listxattr = NULL, + .listxattr = dbfs_op_listxattr, .removexattr = dbfs_op_removexattr, .access = NULL, .create = NULL, @@ -16,6 +16,8 @@ enum { DBFS_XATTR_NAME_LEN = 256, DBFS_XATTR_MAX_LEN = (1024 * 1024), + + DBFS_XLIST_ALIGN = 8, }; enum { @@ -43,6 +45,11 @@ struct dbfs_dirent { char name[0]; } __attribute__ ((packed)); +struct dbfs_xlist { + guint32 namelen; + char name[0]; +} __attribute__ ((packed)); + struct dbfs_extent { dbfs_blk_id_t id; guint64 size; @@ -99,11 +106,12 @@ extern int dbfs_mknod(guint64 parent, const char *name, extern int dbfs_symlink_write(guint64 ino, const char *link); extern int dbfs_inode_del(struct dbfs_inode *ino); extern int dbfs_xattr_get(guint64 ino_n, const char *name, - char **buf_out, size_t *buflen_out); + void **buf_out, size_t *buflen_out); extern int dbfs_xattr_set(guint64 ino_n, const char *name, - const char *buf, size_t buflen, + const void *buf, size_t buflen, int flags); -extern int dbfs_xattr_remove(guint64 ino_n, const char *name); +extern int dbfs_xattr_remove(guint64, const char *, gboolean); +extern int dbfs_xattr_list(guint64 ino, void **buf_out, size_t *buflen_out); /* libdbfs.c */ extern int dbfs_open(struct dbfs *fs); @@ -121,4 +129,9 @@ static inline size_t dbfs_dirent_next(guint16 namelen) return ALIGN(sizeof(struct dbfs_dirent) + namelen, DBFS_DIRENT_ALIGN); } +static inline size_t dbfs_xlist_next(guint16 namelen) +{ + return ALIGN(sizeof(struct dbfs_xlist) + namelen, DBFS_XLIST_ALIGN); +} + #endif /* __DBFS_H__ */ |