X-Git-Url: https://mattmccutchen.net/rsync/rsync-patches.git/blobdiff_plain/a8801c39b7925ea9becfe29a410a95ede69aa1bc..9978080ec09f62c426e6a1b81598a2f33bd91cd1:/xattrs.diff diff --git a/xattrs.diff b/xattrs.diff index ebb902c..fc5cf1b 100644 --- a/xattrs.diff +++ b/xattrs.diff @@ -4,26 +4,25 @@ After applying this patch, run these commands for a successful build: autoconf autoheader - ./configure --with-acl-support --with-xattr-support + ./configure --enable-acl-support --enable-xattr-support make proto make ---- orig/Makefile.in 2004-10-20 06:32:26 -+++ Makefile.in 2005-03-03 01:20:46 -@@ -27,14 +27,14 @@ VERSION=@VERSION@ +--- orig/Makefile.in 2005-11-07 04:31:05 ++++ Makefile.in 2005-11-07 04:38:36 +@@ -27,13 +27,13 @@ VERSION=@VERSION@ HEADERS=byteorder.h config.h errcode.h proto.h rsync.h smb_acls.h lib/pool_alloc.h LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o \ - lib/permstring.o lib/pool_alloc.o lib/sysacls.o @LIBOBJS@ + lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattr.o @LIBOBJS@ - ZLIBOBJ=zlib/deflate.o zlib/infblock.o zlib/infcodes.o zlib/inffast.o \ - zlib/inflate.o zlib/inftrees.o zlib/infutil.o zlib/trees.o \ - zlib/zutil.o zlib/adler32.o + ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \ + zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o OBJS1=rsync.o generator.o receiver.o cleanup.o sender.o exclude.o util.o \ main.o checksum.o match.o syscall.o log.o backup.o OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \ -- fileio.o batch.o clientname.o acls.o -+ fileio.o batch.o clientname.o acls.o xattr.o +- fileio.o batch.o clientname.o chmod.o acls.o ++ fileio.o batch.o clientname.o chmod.o acls.o xattr.o OBJS3=progress.o pipe.o DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \ @@ -37,17 +36,17 @@ After applying this patch, run these commands for a successful build: } } *p = '/'; -@@ -189,6 +190,7 @@ static int keep_backup(char *fname) +@@ -190,6 +191,7 @@ static int keep_backup(char *fname) return 0; PUSH_KEEP_BACKUP_ACL(file, fname, buf); + PUSH_KEEP_BACKUP_XATTR(file, fname, buf); /* Check to see if this is a device file, or link */ - if (IS_DEVICE(file->mode)) { -@@ -264,6 +266,7 @@ static int keep_backup(char *fname) + if ((am_root && preserve_devices && IS_DEVICE(file->mode)) +@@ -267,6 +269,7 @@ static int keep_backup(char *fname) } - set_perms(buf, file, NULL, 0); + set_file_attrs(buf, file, NULL, 0); CLEANUP_KEEP_BACKUP_ACL(); + CLEANUP_KEEP_BACKUP_XATTR(); free(file); @@ -55,15 +54,15 @@ After applying this patch, run these commands for a successful build: if (verbose > 1) { --- orig/configure.in 2004-08-19 19:53:27 +++ configure.in 2005-05-12 22:57:53 -@@ -810,6 +810,30 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_ +@@ -826,6 +826,30 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_ AC_MSG_RESULT(no) ) +AC_CHECK_HEADERS(attr/xattr.h) +AC_MSG_CHECKING(whether to support extended attributes) -+AC_ARG_WITH(xattr-support, -+[ --with-xattr-support Include extended attribute support (default=no)], -+[ case "$withval" in ++AC_ARG_ENABLE(xattr-support, ++AC_HELP_STRING([--enable-xattr-support], [Include extended attribute support (default=no)]), ++[ case "$enableval" in + yes) + case "$host_os" in + *linux*) @@ -86,20 +85,20 @@ After applying this patch, run these commands for a successful build: AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig]) AC_OUTPUT ---- orig/flist.c 2005-03-16 02:24:11 -+++ flist.c 2005-05-12 22:55:41 -@@ -976,6 +976,8 @@ static struct file_struct *send_file_nam +--- orig/flist.c 2006-01-31 02:37:33 ++++ flist.c 2005-10-16 23:03:04 +@@ -969,6 +969,8 @@ static struct file_struct *send_file_nam return NULL; - if (!MAKE_ACL(file, fname)) + if (MAKE_ACL(file, fname) < 0) return NULL; -+ if (!MAKE_XATTR(file, fname)) ++ if (MAKE_XATTR(file, fname) < 0) + return NULL; - maybe_emit_filelist_progress(flist->count + flist_count_offset); - -@@ -985,9 +987,11 @@ static struct file_struct *send_file_nam + if (chmod_modes && !S_ISLNK(file->mode)) + file->mode = tweak_mode(file->mode, chmod_modes); +@@ -981,9 +983,11 @@ static struct file_struct *send_file_nam flist->files[flist->count++] = file; - send_file_entry(file, f, base_flags); + send_file_entry(file, f); SEND_ACL(file, f); + SEND_XATTR(file, f); } else { @@ -109,7 +108,7 @@ After applying this patch, run these commands for a successful build: } return file; } -@@ -1315,6 +1319,7 @@ struct file_list *recv_file_list(int f) +@@ -1373,6 +1377,7 @@ struct file_list *recv_file_list(int f) file = receive_file_entry(flist, flags, f); RECEIVE_ACL(file, f); @@ -117,17 +116,17 @@ After applying this patch, run these commands for a successful build: if (S_ISREG(file->mode)) stats.total_size += file->length; -@@ -1339,6 +1344,7 @@ struct file_list *recv_file_list(int f) +@@ -1397,6 +1402,7 @@ struct file_list *recv_file_list(int f) clean_flist(flist, relative_paths, 1); SORT_FILE_ACL_INDEX_LISTS(); + SORT_FILE_XATTR_INDEX_LISTS(); if (f >= 0) { - /* Now send the uid/gid list. This was introduced in ---- orig/generator.c 2005-05-12 23:34:00 + recv_uid_list(f, flist); +--- orig/generator.c 2006-01-31 02:35:44 +++ generator.c 2005-05-12 23:21:08 -@@ -724,6 +724,10 @@ static void recv_generator(char *fname, +@@ -899,6 +899,10 @@ static void recv_generator(char *fname, if (f_out == -1) SET_ACL(fname, file); #endif @@ -137,7 +136,7 @@ After applying this patch, run these commands for a successful build: +#endif if (delete_during && f_out != -1 && !phase && dry_run < 2 && (file->flags & FLAG_DEL_HERE)) - delete_in_dir(the_file_list, fname, file); + delete_in_dir(the_file_list, fname, file, &st); --- orig/lib/sysxattr.c 2005-05-12 23:23:15 +++ lib/sysxattr.c 2005-05-12 23:23:15 @@ -0,0 +1,41 @@ @@ -194,17 +193,17 @@ After applying this patch, run these commands for a successful build: +#else + +#endif /* No xattrs */ ---- orig/options.c 2005-05-12 23:34:38 -+++ options.c 2005-05-12 23:21:25 +--- orig/options.c 2006-01-31 03:11:18 ++++ options.c 2006-01-31 03:16:24 @@ -45,6 +45,7 @@ int copy_links = 0; int preserve_links = 0; int preserve_hard_links = 0; int preserve_acls = 0; +int preserve_xattrs = 0; int preserve_perms = 0; + int preserve_executability = 0; int preserve_devices = 0; - int preserve_uid = 0; -@@ -182,6 +183,7 @@ static void print_rsync_version(enum log +@@ -195,6 +196,7 @@ static void print_rsync_version(enum log char const *have_inplace = "no "; char const *hardlinks = "no "; char const *acls = "no "; @@ -212,7 +211,7 @@ After applying this patch, run these commands for a successful build: char const *links = "no "; char const *ipv6 = "no "; STRUCT_STAT *dumstat; -@@ -201,7 +203,9 @@ static void print_rsync_version(enum log +@@ -214,7 +216,9 @@ static void print_rsync_version(enum log #ifdef SUPPORT_ACLS acls = ""; #endif @@ -223,8 +222,8 @@ After applying this patch, run these commands for a successful build: #ifdef SUPPORT_LINKS links = ""; #endif -@@ -216,9 +220,9 @@ static void print_rsync_version(enum log - "Copyright (C) 1996-2005 by Andrew Tridgell and others\n"); +@@ -228,9 +232,9 @@ static void print_rsync_version(enum log + rprintf(f, "Copyright (C) 1996-2006 by Andrew Tridgell, Wayne Davison, and others.\n"); rprintf(f, "\n"); rprintf(f, "Capabilities: %d-bit files, %ssocketpairs, " - "%shard links, %sACLs, %ssymlinks, batchfiles, \n", @@ -235,41 +234,43 @@ After applying this patch, run these commands for a successful build: /* Note that this field may not have type ino_t. It depends * on the complicated interaction between largefile feature -@@ -289,6 +293,7 @@ void usage(enum logcode F) - rprintf(F," -K, --keep-dirlinks treat symlinked dir on receiver as dir\n"); +@@ -300,6 +304,7 @@ void usage(enum logcode F) rprintf(F," -p, --perms preserve permissions\n"); + rprintf(F," -E, --executability preserve the file's executability\n"); rprintf(F," -A, --acls preserve ACLs (implies --perms)\n"); + rprintf(F," -X, --xattrs preserve extended attributes (implies --perms)\n"); - rprintf(F," -o, --owner preserve owner (root only)\n"); + rprintf(F," --chmod=CHMOD change destination permissions\n"); + rprintf(F," -o, --owner preserve owner (super-user only)\n"); rprintf(F," -g, --group preserve group\n"); - rprintf(F," -D, --devices preserve devices (root only)\n"); -@@ -417,6 +422,7 @@ static struct poptOption long_options[] - {"copy-unsafe-links",0, POPT_ARG_NONE, ©_unsafe_links, 0, 0, 0 }, - {"perms", 'p', POPT_ARG_NONE, &preserve_perms, 0, 0, 0 }, - {"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 }, -+ {"xattrs", 'X', POPT_ARG_NONE, 0, 'X', 0, 0 }, - {"owner", 'o', POPT_ARG_NONE, &preserve_uid, 0, 0, 0 }, - {"group", 'g', POPT_ARG_NONE, &preserve_gid, 0, 0, 0 }, - {"devices", 'D', POPT_ARG_NONE, &preserve_devices, 0, 0, 0 }, -@@ -907,6 +913,17 @@ int parse_arguments(int *argc, const cha +@@ -419,6 +424,9 @@ static struct poptOption long_options[] + {"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 }, + {"no-acls", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 }, + {"no-A", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 }, ++ {"xattrs", 'X', POPT_ARG_NONE, 0, 'X', 0, 0 }, ++ {"no-xattrs", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 }, ++ {"no-X", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 }, + {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 }, + {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 }, + {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 }, +@@ -1084,6 +1092,17 @@ int parse_arguments(int *argc, const cha + return 0; #endif /* SUPPORT_ACLS */ - break; + case 'X': +#ifdef SUPPORT_XATTRS + preserve_xattrs = 1; + preserve_perms = 1; ++ break; +#else + snprintf(err_buf,sizeof(err_buf), + "extended attributes are not supported on this %s\n", + am_server ? "server" : "client"); + return 0; +#endif /* SUPPORT_XATTRS */ -+ break; default: /* A large opt value means that set_refuse_options() -@@ -1314,6 +1331,8 @@ void server_options(char **args,int *arg +@@ -1527,6 +1546,8 @@ void server_options(char **args,int *arg argstr[x++] = 'H'; if (preserve_acls) argstr[x++] = 'A'; @@ -278,26 +279,30 @@ After applying this patch, run these commands for a successful build: if (preserve_uid) argstr[x++] = 'o'; if (preserve_gid) ---- orig/rsync.c 2004-07-03 20:11:58 -+++ rsync.c 2005-03-03 01:31:22 -@@ -144,6 +144,14 @@ int set_perms(char *fname,struct file_st +--- orig/rsync.c 2006-01-31 02:33:51 ++++ rsync.c 2005-10-16 23:19:27 +@@ -163,12 +163,15 @@ int set_file_attrs(char *fname, struct f + } + #endif + +- /* If this is a directory, SET_ACL() will be called on the cleanup +- * receive_generator() pass (if we called it here, we might clobber +- * writability on the directory). Everything else is OK to do now. */ ++ /* If this is a directory, SET_ACL() and/or SET_XATTR() will be called ++ * on the cleanup receive_generator() pass -- if we called it here, we ++ * might clobber writability on the dir (SELinux security contexts are ++ * stored in xattrs). Everything else is OK to do now. */ + if (!S_ISDIR(st->st_mode)) { if (SET_ACL(fname, file) == 0) updated = 1; - } -+ /* If this is a directory, SET_XATTR() will be called on the cleanup -+ * receive_generator() pass--if we called it here, we might clobber -+ * writability on the directory (SELinux security contexts are stored -+ * in xattrs). everything else is OK to do now. */ -+ if (!S_ISDIR(st->st_mode)) { + if (SET_XATTR(fname, file) == 0) -+ updated = 1; -+ } ++ updated = 1; + } - if (verbose > 1 && flags & PERMS_REPORT) { - enum logcode code = daemon_log_format_has_i || dry_run ---- orig/rsync.h 2005-05-12 23:32:56 -+++ rsync.h 2005-05-12 23:19:46 -@@ -676,6 +676,38 @@ struct stats { + if (verbose > 1 && flags & ATTRS_REPORT) { +--- orig/rsync.h 2006-01-23 21:24:53 ++++ rsync.h 2005-10-16 23:19:44 +@@ -688,6 +688,38 @@ struct chmod_mode_struct; #endif /* SUPPORT_ACLS */ #include "smb_acls.h" @@ -325,42 +330,42 @@ After applying this patch, run these commands for a successful build: +#define SEND_XATTR(file, f) +#define RECEIVE_XATTR(file, f) +#define SORT_FILE_XATTR_INDEX_LISTS() -+#define SET_XATTR(fname, file) 0 /* checked return value */ ++#define SET_XATTR(fname, file) 1 /* checked return value */ +#define NEXT_XATTR_UID() +#define XATTR_UID_MAP(uid) +#define PUSH_KEEP_BACKUP_XATTR(file, orig, dest) +#define CLEANUP_KEEP_BACKUP_XATTR() -+#define DUP_XATTR(src, orig) 0 /* checked return value */ ++#define DUP_XATTR(src, orig) 1 /* checked return value */ +#endif /* SUPPORT_XATTRS */ + #include "proto.h" /* We have replacement versions of these if they're missing. */ ---- orig/rsync.yo 2004-07-03 20:11:58 -+++ rsync.yo 2005-03-03 01:33:53 +--- orig/rsync.yo 2006-01-31 03:14:05 ++++ rsync.yo 2006-01-31 03:16:46 @@ -318,6 +318,7 @@ to the detailed description below for a - -K, --keep-dirlinks treat symlinked dir on receiver as dir -p, --perms preserve permissions - -A, --acls preserve ACLs (implies -p) [local option] -+ -X, --xattrs preserve extended attributes (implies -p) [local option] - -o, --owner preserve owner (root only) + -E, --executability preserve executability + -A, --acls preserve ACLs (implies -p) [non-standard] ++ -X, --xattrs preserve extended attrs (implies -p) [n.s.] + --chmod=CHMOD change destination permissions + -o, --owner preserve owner (super-user only) -g, --group preserve group - -D, --devices preserve devices (root only) -@@ -635,6 +636,11 @@ ACLs to be the same as the local ACLs. - remote machine's rsync supports this option also. This is a non-standard - option. +@@ -727,6 +728,11 @@ dit(bf(-A, --acls)) This option causes r + ACLs to be the same as the source ACLs. This nonstandard option only + works if the remote rsync also supports it. bf(--acls) implies bf(--perms). +dit(bf(-X, --xattrs)) This option causes rsync to update the remote +extended attributes to be the same as the local ones. This will work +only if the remote machine's rsync supports this option also. This is +a non-standard option. + - dit(bf(-o, --owner)) This option causes rsync to set the owner of the - destination file to be the same as the source file. On most systems, - only the super-user can set file ownership. By default, the preservation ---- orig/xattr.c 2005-05-13 06:10:34 -+++ xattr.c 2005-05-13 06:10:34 -@@ -0,0 +1,546 @@ + dit(bf(--chmod)) This option tells rsync to apply one or more + comma-separated "chmod" strings to the permission of the files in the + transfer. The resulting value is treated as though it was the permissions +--- orig/xattr.c 2005-10-16 23:25:12 ++++ xattr.c 2005-10-16 23:25:12 +@@ -0,0 +1,540 @@ +/* Extended Attribute support for rsync */ +/* Copyright (C) 2004 Red Hat, Inc */ +/* Written by Jay Fenlason, vaguely based on the ACLs patch */ @@ -471,7 +476,7 @@ After applying this patch, run these commands for a successful build: + return strcmp(xa1->name, xa2->name); +} + -+static BOOL rsync_xal_get(const char *fname, rsync_xal *x) ++static int rsync_xal_get(const char *fname, rsync_xal *x) +{ + ssize_t name_size; + ssize_t datum_size; @@ -496,13 +501,13 @@ After applying this patch, run these commands for a successful build: + } + if (name_size < 0) { + if (errno == ENOTSUP) -+ return False; ++ return -1; + if (errno == ERANGE) { + name_size = sys_llistxattr(fname, NULL, 0); + if (name_size < 0) { + rprintf(FERROR, "%s: rsync_xal_get: llistxattr: %s\n", -+ fname, strerror(errno)); -+ return False; ++ fname, strerror(errno)); ++ return -1; + } + namebuf = realloc_array(namebuf, char, name_size + 1); + if (!namebuf) @@ -513,18 +518,18 @@ After applying this patch, run these commands for a successful build: + rprintf(FERROR, + "%s: rsync_xal_get: re-llistxattr failed: %s\n", + fname, strerror(errno)); -+ return False; ++ return -1; + } + } else { + rprintf(FERROR, + "%s: rsync_xal_get: llistxattr failed: %s\n", + fname, strerror(errno)); -+ return False; ++ return -1; + } + } + rsync_xal_free(x); + if (name_size == 0) -+ return True; ++ return 0; + for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) { + len = strlen(name) + 1; + @@ -546,14 +551,14 @@ After applying this patch, run these commands for a successful build: + } + if (datum_size < 0) { + if (errno == ENOTSUP) -+ return False; ++ return -1; + if (errno == ERANGE) { + datum_size = sys_lgetxattr(fname, name, NULL, 0); + if (datum_size < 0) { + rprintf(FERROR, + "%s: rsync_xal_get: lgetxattr %s failed: %s\n", + fname, name, strerror(errno)); -+ return False; ++ return -1; + } + datumbuf = realloc_array(datumbuf, char, datum_size + 1); + if (!datumbuf) @@ -564,13 +569,13 @@ After applying this patch, run these commands for a successful build: + rprintf(FERROR, + "%s: rsync_xal_get: re-lgetxattr of %s failed: %s\n", + name, fname, strerror(errno)); -+ return False; ++ return -1; + } + } else { + rprintf(FERROR, + "%s: rsync_xal_get: lgetxattr %s failed: %s\n", + fname, name, strerror(errno)); -+ return False; ++ return -1; + } + } + ptr = new_array(char, len + datum_size); @@ -588,20 +593,20 @@ After applying this patch, run these commands for a successful build: + if (x->count > 1) { + qsort(x->rxas, x->count, sizeof (rsync_xa), rsync_xal_compare_names); + } -+ return True; ++ return 0; +} + + +/* generate the xattr(s) for this flist entry; + * xattr(s) are either sent or cleaned-up by send_xattr() below */ + -+BOOL make_xattr(const struct file_struct *file, const char *fname) ++int make_xattr(const struct file_struct *file, const char *fname) +{ + if (!preserve_xattrs || !file) -+ return True; ++ return 1; + + rsync_xal_get(fname, &curr_rsync_xal); -+ return True; ++ return 0; /* TODO: This needs to return 1 if no xattrs changed! */ +} + +static ssize_t rsync_xal_find_matching(void) @@ -697,7 +702,7 @@ After applying this patch, run these commands for a successful build: + + if (!preserve_xattrs) + return; -+ fname = f_name(file); ++ fname = f_name(file, NULL); + tag = read_byte(f); + if (tag != 'X' && tag != 'x') { + rprintf(FERROR, @@ -764,7 +769,7 @@ After applying this patch, run these commands for a successful build: + index = read_int(f); + if (index >= rsync_xal_l.count) { + rprintf(FERROR, "%s: receive_xattr: xa index %lu out of range\n", -+fname, (unsigned long)index); ++ fname, (unsigned long)index); + exit_cleanup(RERR_STREAMIO); + } + fxil.filexalidxs[fxil.count].xalidx = index; @@ -772,19 +777,17 @@ After applying this patch, run these commands for a successful build: + fxil.count++; +} + -+static BOOL rsync_xal_set(const char *fname, rsync_xal *x) ++static int rsync_xal_set(const char *fname, rsync_xal *x) +{ + size_t i; -+ int status; -+ BOOL ret; ++ int ret = 0; + -+ ret = True; + for (i = 0; i < x->count; i++) { -+ status = sys_lsetxattr(fname, x->rxas[i].name, x->rxas[i].datum, x->rxas[i].datum_len, 0); ++ int status = sys_lsetxattr(fname, x->rxas[i].name, x->rxas[i].datum, x->rxas[i].datum_len, 0); + if (status < 0) { + rprintf(FERROR, "%s: rsync_xal_set: lsetxattr %s failed: %s\n", + fname, x->rxas[i].name, strerror(errno)); -+ ret = False; ++ ret = -1; + } + } + return ret; @@ -796,13 +799,13 @@ After applying this patch, run these commands for a successful build: +{ + int ret; + -+ ret = 0; + if (!preserve_xattrs) -+ return ret; ++ return 1; + -+ ret = rsync_xal_get(orig, &backup_xal); -+ if (ret == True) ++ if (rsync_xal_get(orig, &backup_xal) < 0) + ret = rsync_xal_set(bak, &backup_xal); ++ else ++ ret = 0; + rsync_xal_free(&backup_xal); + return ret; +} @@ -821,7 +824,7 @@ After applying this patch, run these commands for a successful build: +static int set_keep_backup_xal(void) +{ + if (!preserve_xattrs) -+ return 0; ++ return 1; + return rsync_xal_set(backup_dest_fname, &backup_xal); +} + @@ -884,26 +887,22 @@ After applying this patch, run these commands for a successful build: + return -1; +} + -+ +/* set extended attributes on rsync-ed or keep_backup-ed file */ + +int set_xattr(const char *fname, const struct file_struct *file) +{ -+ int updated; + int xalidx; + rsync_xal *x; + -+ updated = 0; + if (dry_run || !preserve_xattrs) -+ return 0; ++ return 1; + if (file == backup_orig_file) { + if (!strcmp(fname, backup_dest_fname)) + return set_keep_backup_xal(); + } + xalidx = find_file_xal_index(file); + x = &(rsync_xal_l.rxals[xalidx]); -+ updated = rsync_xal_set(fname, x); -+ return updated; ++ return rsync_xal_set(fname, x); +} + +#endif /* SUPPORT_XATTRS */