Index: bin/ls/ls.h =================================================================== --- bin/ls/ls.h +++ bin/ls/ls.h @@ -50,6 +50,8 @@ extern int f_longform; /* long listing format */ extern int f_octal; /* print unprintables in octal */ extern int f_octal_escape; /* like f_octal but use C escapes if possible */ +extern int f_nofollow; /* don't follow symbolic links */ +extern int f_printea; /* Print any exteded attributes on a file */ extern int f_nonprint; /* show unprintables as ? */ extern int f_samesort; /* sort time and name in same direction */ extern int f_sectime; /* print the real time for all files */ Index: bin/ls/ls.1 =================================================================== --- bin/ls/ls.1 +++ bin/ls/ls.1 @@ -32,7 +32,7 @@ .\" @(#)ls.1 8.7 (Berkeley) 7/29/94 .\" $FreeBSD$ .\" -.Dd August 18, 2018 +.Dd May 10, 2020 .Dt LS 1 .Os .Sh NAME @@ -40,7 +40,7 @@ .Nd list directory contents .Sh SYNOPSIS .Nm -.Op Fl ABCFGHILPRSTUWZabcdfghiklmnopqrstuwxy1, +.Op Fl ABCFGHILPRSTUWZabcdfghiklmnopqrstuwxy1@, .Op Fl -color Ns = Ns Ar when .Op Fl D Ar format .Op Ar @@ -432,6 +432,12 @@ option has no effect. This option is not defined in .St -p1003.1-2001 . +.It Fl @ +In conjunction with +.Fl l , +list any extended attributes associated with the file. +This option is not defined in +.St -p1003.1-2001 . .El .Pp The @@ -630,10 +636,18 @@ The next field contains a plus .Pq Ql + -character if the file has an ACL, or a -space -.Pq Ql " " -if it does not. +character if the file has an ACL, or an +at +.Pq Ql @ +character if the file has extended attributes, +or a space if it has neither. +If the file has both an ACL and an extended +attribute, it will show only the at character. +In conjunction with the +.Fl l +option, it will list extended attributes in +the format namespace, extended attribute name, +extended attribute size. The .Nm utility does not show the actual ACL; Index: bin/ls/ls.c =================================================================== --- bin/ls/ls.c +++ bin/ls/ls.c @@ -128,7 +128,8 @@ static int f_listdot; /* list files beginning with . */ int f_longform; /* long listing format */ static int f_noautodot; /* do not automatically enable -A for root */ -static int f_nofollow; /* don't follow symbolic link arguments */ + int f_nofollow; /* don't follow symbolic link arguments */ + int f_printea; /* print any extended attributes on a file */ int f_nonprint; /* show unprintables as ? */ static int f_nosort; /* don't sort output */ int f_notabs; /* don't use tab-separated multi-col output */ @@ -266,7 +267,7 @@ if (getenv("LS_SAMESORT")) f_samesort = 1; while ((ch = getopt_long(argc, argv, - "+1ABCD:FGHILPRSTUWXZabcdfghiklmnopqrstuwxy,", long_opts, + "+1@ABCD:FGHILPRSTUWXZabcdfghiklmnopqrstuwxy,", long_opts, NULL)) != -1) { switch (ch) { /* @@ -281,6 +282,9 @@ case 'C': f_sortacross = f_longform = f_singlecol = 0; break; + case '@': + f_printea = 1; + break; case 'l': f_longform = 1; f_singlecol = 0; Index: bin/ls/print.c =================================================================== --- bin/ls/print.c +++ bin/ls/print.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -78,7 +79,9 @@ static void endcolor(int); static int colortype(mode_t); #endif -static void aclmode(char *, const FTSENT *); +static int check_acl(const FTSENT *); +static int check_extended_attr(const FTSENT *); +static void print_extattrs(const FTSENT *, int); #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) @@ -211,6 +214,7 @@ FTSENT *p; NAMES *np; char buf[20]; + int acl, extattr; #ifdef COLORLS int color_printed = 0; #endif @@ -231,7 +235,12 @@ (void)printf("%*jd ", dp->s_block, howmany(sp->st_blocks, blocksize)); strmode(sp->st_mode, buf); - aclmode(buf, p); + extattr = check_extended_attr(p); + acl = check_acl(p); + if (extattr) + buf[10] = '@'; + else if (acl) + buf[10] = '+'; np = p->fts_pointer; (void)printf("%s %*ju %-*s %-*s ", buf, dp->s_nlink, (uintmax_t)sp->st_nlink, dp->s_user, np->user, dp->s_group, @@ -266,6 +275,10 @@ if (S_ISLNK(sp->st_mode)) printlink(p); (void)putchar('\n'); + if (f_printea && extattr) { + print_extattrs(p, EXTATTR_NAMESPACE_USER); + print_extattrs(p, EXTATTR_NAMESPACE_SYSTEM); + } } } @@ -759,8 +772,8 @@ * Add a + after the standard rwxrwxrwx mode if the file has an * ACL. strmode() reserves space at the end of the string. */ -static void -aclmode(char *buf, const FTSENT *p) +static int +check_acl(const FTSENT *p) { char name[MAXPATHLEN + 1]; int ret, trivial; @@ -775,10 +788,10 @@ */ if (S_ISCHR(p->fts_statp->st_mode) || S_ISBLK(p->fts_statp->st_mode) || S_ISWHT(p->fts_statp->st_mode)) - return; + return (0); if (previous_dev == p->fts_statp->st_dev && supports_acls == 0) - return; + return (0); if (p->fts_level == FTS_ROOTLEVEL) snprintf(name, sizeof(name), "%s", p->fts_name); @@ -796,7 +809,7 @@ supports_acls = 1; } else if (ret < 0 && errno != EINVAL) { warn("%s", name); - return; + return (0); } if (supports_acls == 0) { ret = lpathconf(name, _PC_ACL_EXTENDED); @@ -805,23 +818,112 @@ supports_acls = 1; } else if (ret < 0 && errno != EINVAL) { warn("%s", name); - return; + return (0); } } } if (supports_acls == 0) - return; + return (0); facl = acl_get_link_np(name, type); if (facl == NULL) { warn("%s", name); - return; + return (0); } if (acl_is_trivial_np(facl, &trivial)) { acl_free(facl); warn("%s", name); - return; + return (0); } - if (!trivial) - buf[10] = '+'; acl_free(facl); + if (!trivial) + return (1); + return (0); +} + +/* + * Print out extended atttributes and their values. It prints + * them in the format "\t \t\n". + * It prints nothing on error. + */ +static void +print_extattrs(const FTSENT *p, int namespace) +{ + char name[MAXPATHLEN + 1]; + char *ns_name; + struct { unsigned char namelen; char name[]; } *ea_entry; + ssize_t ea_total; + void *ea_list = NULL; + void *ea_list_end; + ssize_t (*lsfunc)(const char *, int, void *, size_t); + ssize_t (*gfunc)(const char *, int, const char *, void *, size_t); + + lsfunc = f_nofollow ? extattr_list_link : extattr_list_file; + gfunc = f_nofollow ? extattr_get_link : extattr_get_file; + + if (extattr_namespace_to_string(namespace, &ns_name) == -1) + return; + + if (p->fts_level == FTS_ROOTLEVEL) + snprintf(name, sizeof(name), "%s", p->fts_name); + else + snprintf(name, sizeof(name), "%s/%s", + p->fts_parent->fts_accpath, p->fts_name); + ea_total = (*lsfunc)(name, namespace, NULL, 0); + if (ea_total <= 0) + return; + ea_list = malloc(ea_total); + if (ea_list == NULL) + return; + + ea_total = (*lsfunc)(name, namespace, ea_list, ea_total); + if (ea_total <= 0) { + free(ea_list); + return; + } + ea_list_end = (uint8_t*)ea_list + ea_total; + for (ea_entry = ea_list; + (void*)ea_entry < ea_list_end; + ea_entry = (void*)((uint8_t*)ea_entry + ea_entry->namelen + 1)) { + char ea_name[ea_entry->namelen+1]; + ssize_t ea_length; + + memcpy(ea_name, ea_entry->name, ea_entry->namelen); + ea_name[ea_entry->namelen] = 0; + ea_length = (*gfunc)(name, namespace, ea_name, NULL, 0); + if (ea_length == -1) + continue; + printf("\t%s %s\t%zu\n", ns_name, ea_name, ea_length); + } + free(ea_list); + return; +} + +/* + * Determine if the file has extended attributes. This only looks at + * EXTATTR_NAMESPACE_USER and EXTATTR_NAMESPACE_SYSTEM; if permission + * is not allowed for either, then it behaves as if there are none. + */ +static int +check_extended_attr(const FTSENT *p) +{ + char name[MAXPATHLEN + 1]; + ssize_t ea_sizes; + ssize_t (*func)(const char *, int, void *, size_t); + + func = f_nofollow ? extattr_list_link : extattr_list_file; + + if (p->fts_level == FTS_ROOTLEVEL) + snprintf(name, sizeof(name), "%s", p->fts_name); + else + snprintf(name, sizeof(name), "%s/%s", + p->fts_parent->fts_accpath, p->fts_name); + + ea_sizes = (*func)(name, EXTATTR_NAMESPACE_USER, NULL, 0); + + if (ea_sizes > 0) + return (1); + + ea_sizes = (*func)(name, EXTATTR_NAMESPACE_SYSTEM, NULL, 0); + + return (ea_sizes > 0); }