fsstress: add operation for setting xattrs on files and directories
authorFilipe Manana <fdmanana@suse.com>
Fri, 19 Apr 2019 10:54:08 +0000 (11:54 +0100)
committerEryu Guan <guaneryu@gmail.com>
Sat, 20 Apr 2019 07:34:15 +0000 (15:34 +0800)
Currently fsstress does not exercise creating, reading or deleting xattrs
on files or directories. This change adds support for setting xattrs on
files and directories, using only the xattr user namespace (the other
namespaces are not general purpose and are used for security, capabilities,
ACLs, etc). This adds a counter for each file entry structure that keeps
track of the number of xattrs set for the file entry, and each new xattr
has a name that includes the counter's value (example: "user.x4").
Values for the xattrs have at most 100 bytes, which is more than the
maximum size supported for all major filesystems.

Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: Eryu Guan <guaneryu@gmail.com>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
ltp/fsstress.c

index 3ec1914..b3ff8d9 100644 (file)
@@ -27,6 +27,7 @@
 io_context_t   io_ctx;
 #endif
 #include <sys/syscall.h>
+#include <sys/xattr.h>
 
 #ifndef FS_IOC_GETFLAGS
 #define FS_IOC_GETFLAGS                 _IOR('f', 1, long)
@@ -84,6 +85,7 @@ typedef enum {
        OP_RESVSP,
        OP_RMDIR,
        OP_SETATTR,
+       OP_SETFATTR,
        OP_SETXATTR,
        OP_SPLICE,
        OP_STAT,
@@ -110,6 +112,7 @@ typedef struct opdesc {
 typedef struct fent {
        int     id;
        int     parent;
+       int     xattr_counter;
 } fent_t;
 
 typedef struct flist {
@@ -156,6 +159,8 @@ struct print_string {
 #define        MAXFSIZE        ((1ULL << 63) - 1ULL)
 #define        MAXFSIZE32      ((1ULL << 40) - 1ULL)
 
+#define XATTR_NAME_BUF_SIZE 18
+
 void   afsync_f(int, long);
 void   allocsp_f(int, long);
 void   aread_f(int, long);
@@ -176,6 +181,7 @@ void        fdatasync_f(int, long);
 void   fiemap_f(int, long);
 void   freesp_f(int, long);
 void   fsync_f(int, long);
+char   *gen_random_string(int);
 void   getattr_f(int, long);
 void   getdents_f(int, long);
 void   link_f(int, long);
@@ -194,6 +200,7 @@ void        rename_f(int, long);
 void   resvsp_f(int, long);
 void   rmdir_f(int, long);
 void   setattr_f(int, long);
+void   setfattr_f(int, long);
 void   setxattr_f(int, long);
 void   splice_f(int, long);
 void   stat_f(int, long);
@@ -204,6 +211,7 @@ void        unlink_f(int, long);
 void   unresvsp_f(int, long);
 void   write_f(int, long);
 void   writev_f(int, long);
+char   *xattr_flag_to_string(int);
 
 opdesc_t       ops[] = {
      /* { OP_ENUM, "name", function, freq, iswrite }, */
@@ -244,7 +252,11 @@ opdesc_t   ops[] = {
        { OP_RENAME, "rename", rename_f, 2, 1 },
        { OP_RESVSP, "resvsp", resvsp_f, 1, 1 },
        { OP_RMDIR, "rmdir", rmdir_f, 1, 1 },
+       /* set attribute flag (FS_IOC_SETFLAGS ioctl) */
        { OP_SETATTR, "setattr", setattr_f, 0, 1 },
+       /* set extended attribute */
+       { OP_SETFATTR, "setfattr", setfattr_f, 2, 1 },
+       /* set project id (XFS_IOC_FSSETXATTR ioctl) */
        { OP_SETXATTR, "setxattr", setxattr_f, 1, 1 },
        { OP_SPLICE, "splice", splice_f, 1, 1 },
        { OP_STAT, "stat", stat_f, 1, 0 },
@@ -296,7 +308,7 @@ char                *execute_cmd = NULL;
 int            execute_freq = 1;
 struct print_string    flag_str = {0};
 
-void   add_to_flist(int, int, int);
+void   add_to_flist(int, int, int, int);
 void   append_pathname(pathname_t *, char *);
 int    attr_list_path(pathname_t *, char *, const int, int, attrlist_cursor_t *);
 int    attr_remove_path(pathname_t *, const char *, int);
@@ -315,6 +327,7 @@ int fent_to_name(pathname_t *, flist_t *, fent_t *);
 void   fix_parent(int, int);
 void   free_pathname(pathname_t *);
 int    generate_fname(fent_t *, int, pathname_t *, int *, int *);
+int    generate_xattr_name(int, char *, int);
 int    get_fname(int, long, pathname_t *, flist_t **, fent_t **, int *);
 void   init_pathname(pathname_t *);
 int    lchown_path(pathname_t *, uid_t, gid_t);
@@ -716,7 +729,7 @@ translate_flags(int flags, const char *delim,
 }
 
 void
-add_to_flist(int ft, int id, int parent)
+add_to_flist(int ft, int id, int parent, int xattr_counter)
 {
        fent_t  *fep;
        flist_t *ftp;
@@ -729,6 +742,7 @@ add_to_flist(int ft, int id, int parent)
        fep = &ftp->fents[ftp->nfiles++];
        fep->id = id;
        fep->parent = parent;
+       fep->xattr_counter = xattr_counter;
 }
 
 void
@@ -1122,6 +1136,18 @@ generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v)
        return 1;
 }
 
+int generate_xattr_name(int xattr_num, char *buffer, int buflen)
+{
+       int ret;
+
+       ret = snprintf(buffer, buflen, "user.x%d", xattr_num);
+       if (ret < 0)
+               return ret;
+       if (ret < buflen)
+               return 0;
+       return -EOVERFLOW;
+}
+
 /*
  * Get file 
  * Input: "which" to choose the file-types eg. non-directory
@@ -3030,7 +3056,7 @@ creat_f(int opno, long r)
                        if (xfsctl(f.path, fd, XFS_IOC_FSSETXATTR, &a) < 0)
                                e1 = errno;
                }
-               add_to_flist(type, id, parid);
+               add_to_flist(type, id, parid, 0);
                close(fd);
        }
        if (v) {
@@ -3495,6 +3521,28 @@ fsync_f(int opno, long r)
        close(fd);
 }
 
+char *
+gen_random_string(int len)
+{
+       static const char charset[] = "0123456789"
+               "abcdefghijklmnopqrstuvwxyz"
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+       int i;
+       char *s;
+
+       if (len == 0)
+               return NULL;
+
+       s = malloc(len);
+       if (!s)
+               return NULL;
+
+       for (i = 0; i < len; i++)
+               s[i] = charset[random() % sizeof(charset)];
+
+       return s;
+}
+
 void
 getattr_f(int opno, long r)
 {
@@ -3551,6 +3599,7 @@ link_f(int opno, long r)
        int             e;
        pathname_t      f;
        fent_t          *fep;
+       fent_t          *fep_src;
        flist_t         *flp;
        int             id;
        pathname_t      l;
@@ -3559,7 +3608,7 @@ link_f(int opno, long r)
        int             v1;
 
        init_pathname(&f);
-       if (!get_fname(FT_NOTDIR, r, &f, &flp, NULL, &v1)) {
+       if (!get_fname(FT_NOTDIR, r, &f, &flp, &fep_src, &v1)) {
                if (v1)
                        printf("%d/%d: link - no file\n", procid, opno);
                free_pathname(&f);
@@ -3586,7 +3635,7 @@ link_f(int opno, long r)
        e = link_path(&f, &l) < 0 ? errno : 0;
        check_cwd();
        if (e == 0)
-               add_to_flist(flp - flist, id, parid);
+               add_to_flist(flp - flist, id, parid, fep_src->xattr_counter);
        if (v) {
                printf("%d/%d: link %s %s %d\n", procid, opno, f.path, l.path,
                        e);
@@ -3626,7 +3675,7 @@ mkdir_f(int opno, long r)
        e = mkdir_path(&f, 0777) < 0 ? errno : 0;
        check_cwd();
        if (e == 0)
-               add_to_flist(FT_DIR, id, parid);
+               add_to_flist(FT_DIR, id, parid, 0);
        if (v) {
                printf("%d/%d: mkdir %s %d\n", procid, opno, f.path, e);
                printf("%d/%d: mkdir add id=%d,parent=%d\n", procid, opno, id, parid);
@@ -3664,7 +3713,7 @@ mknod_f(int opno, long r)
        e = mknod_path(&f, S_IFCHR|0444, 0) < 0 ? errno : 0;
        check_cwd();
        if (e == 0)
-               add_to_flist(FT_DEV, id, parid);
+               add_to_flist(FT_DEV, id, parid, 0);
        if (v) {
                printf("%d/%d: mknod %s %d\n", procid, opno, f.path, e);
                printf("%d/%d: mknod add id=%d,parent=%d\n", procid, opno, id, parid);
@@ -4040,12 +4089,14 @@ rename_f(int opno, long r)
        e = rename_path(&f, &newf) < 0 ? errno : 0;
        check_cwd();
        if (e == 0) {
+               int xattr_counter = fep->xattr_counter;
+
                if (flp - flist == FT_DIR) {
                        oldid = fep->id;
                        fix_parent(oldid, id);
                }
                del_from_flist(flp - flist, fep - flp->fents);
-               add_to_flist(flp - flist, id, parid);
+               add_to_flist(flp - flist, id, parid, xattr_counter);
        }
        if (v) {
                printf("%d/%d: rename %s to %s %d\n", procid, opno, f.path,
@@ -4168,6 +4219,81 @@ setattr_f(int opno, long r)
 }
 
 void
+setfattr_f(int opno, long r)
+{
+       int             e;
+       pathname_t      f;
+       fent_t          *fep;
+       int             v;
+       int             value_len;
+       char            name[XATTR_NAME_BUF_SIZE];
+       char            *value = NULL;
+       int             flag = 0;
+       int             xattr_num;
+
+       init_pathname(&f);
+       if (!get_fname(FT_REGFILE | FT_DIRm, r, &f, NULL, &fep, &v)) {
+               if (v)
+                       printf("%d/%d: setfattr - no filename\n", procid, opno);
+               goto out;
+       }
+       check_cwd();
+
+       if ((fep->xattr_counter > 0) && (random() % 2)) {
+               /*
+                * Use an existing xattr name for replacing its value or
+                * create again a xattr that was previously deleted.
+                */
+               xattr_num = (random() % fep->xattr_counter) + 1;
+               if (random() % 2)
+                       flag = XATTR_REPLACE;
+       } else {
+               /* Use a new xattr name. */
+               xattr_num = fep->xattr_counter + 1;
+               /*
+                * Don't always use the create flag because even if our xattr
+                * counter is 0, we may still have xattrs associated to this
+                * file (this happens when xattrs are added to a file through
+                * one of its other hard links), so we can end up updating an
+                * existing xattr too.
+                */
+               if (random() % 2)
+                       flag = XATTR_CREATE;
+       }
+
+       /*
+        * The maximum supported value size depends on the filesystem
+        * implementation, but 100 bytes is a safe value for most filesystems
+        * at least.
+        */
+       value_len = random() % 101;
+       value = gen_random_string(value_len);
+       if (!value && value_len > 0) {
+               if (v)
+                       printf("%d/%d: setfattr - file %s failed to allocate value with %d bytes\n",
+                              procid, opno, f.path, value_len);
+               goto out;
+       }
+       e = generate_xattr_name(xattr_num, name, sizeof(name));
+       if (e < 0) {
+               printf("%d/%d: setfattr - file %s failed to generate xattr name: %d\n",
+                      procid, opno, f.path, e);
+               goto out;
+       }
+
+       e = setxattr(f.path, name, value, value_len, flag) < 0 ? errno : 0;
+       if (e == 0)
+               fep->xattr_counter++;
+       if (v)
+               printf("%d/%d: setfattr file %s name %s flag %s value length %d: %d\n",
+                      procid, opno, f.path, name, xattr_flag_to_string(flag),
+                      value_len, e);
+out:
+       free(value);
+       free_pathname(&f);
+}
+
+void
 stat_f(int opno, long r)
 {
        int             e;
@@ -4229,7 +4355,7 @@ symlink_f(int opno, long r)
        e = symlink_path(val, &f) < 0 ? errno : 0;
        check_cwd();
        if (e == 0)
-               add_to_flist(FT_SYM, id, parid);
+               add_to_flist(FT_SYM, id, parid, 0);
        free(val);
        if (v) {
                printf("%d/%d: symlink %s %d\n", procid, opno, f.path, e);
@@ -4498,3 +4624,13 @@ writev_f(int opno, long r)
        free_pathname(&f);
        close(fd);
 }
+
+char *
+xattr_flag_to_string(int flag)
+{
+       if (flag == XATTR_CREATE)
+               return "create";
+       if (flag == XATTR_REPLACE)
+               return "replace";
+       return "none";
+}