More ls -Z upgrading. Move TOYBOX_SELINUX and TOYBOX_SMACK support from
portability.h to new lib/lsm.h. Update ls.c to use it.

Fix "ls . toys" (two directories when one is . or ..), which was filtering
out the . as something we shouldn't recurse into even though it was explicitly
listed on the command line. For some reason "ls -Z . toys" is still segfaulting
though (but "ls -Z ." isn't), need to figure out why...
diff --git a/lib/lsm.h b/lib/lsm.h
new file mode 100644
index 0000000..b16138a
--- /dev/null
+++ b/lib/lsm.h
@@ -0,0 +1,96 @@
+/* lsm.h - header file for lib directory
+ *
+ * Copyright 2015 Rob Landley <rob@landley.net>
+ */
+
+#if CFG_TOYBOX_SELINUX
+#include <selinux/selinux.h>
+#else
+#define is_selinux_enabled() 0
+#define getcon(...) (-1)
+#define getfilecon(...) (-1)
+#define lgetfilecon(...) (-1)
+#define fgetfilecon(...) (-1)
+#define setfilecon(...) (-1)
+#define lsetfilecon(...) (-1)
+#define fsetfilecon(...) (-1)
+#endif
+
+#if CFG_TOYBOX_SMACK
+#include <sys/smack.h>
+#include <sys/xattr.h>
+#include <linux/xattr.h>
+#else
+#define XATTR_NAME_SMACK 0
+//ssize_t fgetxattr (int fd, char *name, void *value, size_t size);
+#define smack_smackfs_path(...) (-1)
+#define smack_new_label_from_self(...) (-1)
+#define smack_new_label_from_path(...) (-1)
+#define smack_new_label_from_file(...) (-1)
+#define smack_set_label_for_path(...) (-1)
+#define smack_set_label_for_file(...) (-1)
+#endif
+
+// This turns into "return 0" when no LSM and lets code optimize out.
+static inline int lsm_enabled(void)
+{
+  if (CFG_TOYBOX_SMACK) return !!smack_smackfs_path();
+  else return is_selinux_enabled() == 1;
+}
+
+// Fetch this process's lsm context
+static inline char *lsm_context(void)
+{
+  int ok = 0;
+  char *result;
+
+  if (CFG_TOYBOX_SMACK) ok = smack_new_label_from_self(&result) > 0;
+  else ok = getcon(&result) == 0;
+
+  return ok ? result : strdup("?");
+}
+
+static inline int lsm_set_context(char *filename, char *context)
+{
+  if (CFG_TOYBOX_SMACK)
+    return smack_set_label_for_path(filename, XATTR_NAME_SMACK, 1, context);
+  else return setfilecon(filename, context);
+}
+
+static inline int lsm_lset_context(char *filename, char *context)
+{
+  if (CFG_TOYBOX_SMACK)
+    return smack_set_label_for_path(filename, XATTR_NAME_SMACK, 0, context);
+  else return lsetfilecon(filename, context);
+}
+
+static inline int lsm_fset_context(int file, char *context)
+{
+  if (CFG_TOYBOX_SMACK)
+    return smack_set_label_for_file(file, XATTR_NAME_SMACK, context);
+  else return fsetfilecon(file, context);
+}
+
+
+// returns -1 in case of error or else the length of the context */
+// context can be NULL to get the length only */
+static inline int lsm_get_context(char *filename, char **context)
+{
+  if (CFG_TOYBOX_SMACK)
+    return smack_new_label_from_path(filename, XATTR_NAME_SMACK, 1, context);
+  else return getfilecon(filename, context);
+}
+
+static inline int lsm_lget_context(char *filename, char **context)
+{
+  if (CFG_TOYBOX_SMACK)
+    return smack_new_label_from_path(filename, XATTR_NAME_SMACK, 0, context);
+  else return lgetfilecon(filename, context);
+}
+
+static inline int lsm_fget_context(int file, char **context)
+{
+  if (CFG_TOYBOX_SMACK)
+    return smack_new_label_from_file(file, XATTR_NAME_SMACK, context);
+  return fgetfilecon(file, context);
+}
diff --git a/lib/portability.h b/lib/portability.h
index aa1ee48..f83cab6 100644
--- a/lib/portability.h
+++ b/lib/portability.h
@@ -225,6 +225,10 @@
 #define O_NOFOLLOW 0
 #endif
 
+#ifndef O_NOATIME
+#define O_NOATIME 01000000
+#endif
+
 #ifndef O_CLOEXEC
 #define O_CLOEXEC 02000000
 #endif
@@ -247,24 +251,3 @@
 //#define strncpy(...) @@strncpyisbadmmkay@@
 //#define strncat(...) @@strncatisbadmmkay@@
 
-#if CFG_TOYBOX_SELINUX
-#include <selinux/selinux.h>
-#else
-#define is_selinux_enabled() 0
-int getcon(void* con);
-#endif
-
-#if CFG_TOYBOX_SMACK
-#include <sys/smack.h>
-#include <sys/xattr.h>
-#include <linux/xattr.h>
-#else
-#define smack_new_label_from_path(...) (-1)
-#define smack_set_label_for_path(...)  (-1)
-#define smack_set_label_for_self(...)  (-1)
-#define XATTR_NAME_SMACK ""
-#define SMACK_LABEL_LEN  (1) /* for just ? */
-
-ssize_t fgetxattr (int fd, char *name, void *value, size_t size);
-#endif
-
diff --git a/toys/posix/ls.c b/toys/posix/ls.c
index 35d1034..ae0466b 100644
--- a/toys/posix/ls.c
+++ b/toys/posix/ls.c
@@ -5,13 +5,13 @@
  *
  * See http://opengroup.org/onlinepubs/9699919799/utilities/ls.html
 
-USE_LS(NEWTOY(ls, USE_LS_COLOR("(color):;")USE_LS_Z("Z")"goACFHLRSacdfiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL]", TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_LS(NEWTOY(ls, USE_LS_COLOR("(color):;")"ZgoACFHLRSacdfiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL]", TOYFLAG_BIN|TOYFLAG_LOCALE))
 
 config LS
   bool "ls"
   default y
   help
-    usage: ls [-ACFHLRSacdfiklmnpqrstux1] [directory...]
+    usage: ls [-ACFHLRSZacdfiklmnpqrstux1] [directory...]
     list files
 
     what to show:
@@ -22,6 +22,7 @@
     -u	use access time for timestamps		-A  list all files but . and ..
     -H	follow command line symlinks		-L  follow symlinks
     -R	recursively list files in subdirs	-F  append /dir *exe @sym |FIFO
+    -Z	security context
 
     output formats:
     -1	list one file per line			-C  columns (sorted vertically)
@@ -32,15 +33,6 @@
     sorting (default is alphabetical):
     -f	unsorted	-r  reverse	-t  timestamp	-S  size
 
-config LS_Z
-  bool
-  default y
-  depends on LS && (TOYBOX_SELINUX || TOYBOX_SMACK)
-  help
-    usage: ls [-Z]
-
-    -Z	security context
-
 config LS_COLOR
   bool "ls --color"
   default y
@@ -55,6 +47,7 @@
 
 #define FOR_ls
 #include "toys.h"
+#include "lib/lsm.h"
 
 // test sst output (suid/sticky in ls flaglist)
 
@@ -63,7 +56,7 @@
 GLOBALS(
   char *color;
 
-  struct dirtree *files;
+  struct dirtree *files, *singledir;
 
   unsigned screen_width;
   int nl_title;
@@ -129,44 +122,6 @@
   return snprintf(0, 0, "%llu", ll);
 }
 
-// measure/print SELinux/smack security label. (If pad=0, just measure.)
-static unsigned seclabel(struct dirtree *dt, int pad)
-{
-  if (CFG_TOYBOX_SELINUX) {
-    char* path = dirtree_path(dt, 0);
-    char* label = 0;
-    size_t len;
-
-    lgetfilecon(path, &label);
-    if (!label) {
-      label = strdup("?");
-    }
-
-    len = strlen(label);
-    if (pad) printf(" %*s "+(pad>0), pad, label);
-
-    free(label);
-    free(path);
-    return len;
-  } else if (CFG_TOYBOX_SMACK) {
-    int fd = openat(dirtree_parentfd(dt), dt->name, O_PATH|O_NOFOLLOW);
-    char buf[SMACK_LABEL_LEN+1];
-    ssize_t len = 1;
-
-    strcpy(buf, "?");
-    if (fd != -1) {
-      len = fgetxattr(fd, XATTR_NAME_SMACK, pad?buf:0, pad?SMACK_LABEL_LEN:0);
-      close(fd);
-
-      if (len<1 || len>SMACK_LABEL_LEN) len = 0;
-      else buf[len] = 0;
-    }
-    if (pad) printf(" %*s "+(pad>0), pad, buf);
-
-    return len;
-  }
-}
-
 // Figure out size of printable entry fields for display indent/wrap
 
 static void entrylen(struct dirtree *dt, unsigned *len)
@@ -192,7 +147,7 @@
   }
 
   len[6] = (flags & FLAG_s) ? numlen(st->st_blocks) : 0;
-  len[7] = (CFG_LS_Z && (flags & FLAG_Z)) ? seclabel(dt, 0) : 0;
+  len[7] = (flags & FLAG_Z) ? strwidth((char *)dt->extra) : 0;
 }
 
 static int compare(void *a, void *b)
@@ -225,6 +180,32 @@
     return 0;
   }
 
+  if (flags & FLAG_Z) {
+    if (!CFG_TOYBOX_LSM_NONE) {
+      // In theory we can just openat(O_PATH|O_NOFOLLOW) and getcontext() on
+      // that filehandle, but the kernel won't let us read this "metadata"
+      // unless we have permission to read the data, so we do these elaborate
+      // bug workarounds instead.
+      if (S_ISLNK(new->st.st_mode) && !(toys.optflags & FLAG_L)) {
+        char *path;
+
+        // Wouldn't it be nice if the lsm functions worked like openat(),
+        // fchmodat(), mknodat(), readlinkat()... but no, this is 1990's tech.
+        path = dirtree_path(new, 0);
+        lsm_lget_context(path, (char **)&new->extra);
+        free(path);
+      } else {
+        // Why O_NONBLOCK? No idea. Why not O_PATH|O_NOFOLLOW? Because reasons.
+        int fd = openat(dirtree_parentfd(new), new->name,
+          O_RDONLY|O_NONBLOCK|O_NOATIME);
+
+        if (fd != -1) lsm_fget_context(fd, (char **)&new->extra);
+        close(fd);
+      }
+    }
+    if (CFG_TOYBOX_LSM_NONE || !new->extra) new->extra = (long)xstrdup("?");
+  }
+
   if (flags & FLAG_u) new->st.st_mtime = new->st.st_atime;
   if (flags & FLAG_c) new->st.st_mtime = new->st.st_ctime;
   if (flags & FLAG_k) new->st.st_blocks = (new->st.st_blocks + 1) / 2;
@@ -303,18 +284,19 @@
 
   memset(totals, 0, sizeof(totals));
 
-  // Silently descend into single directory listed by itself on command line.
-  // In this case only show dirname/total header when given -R.
+  // Top level directory was already populated by main()
   if (!indir->parent) {
-    if (!(dt = indir->child)) return;
+    // Silently descend into single directory listed by itself on command line.
+    // In this case only show dirname/total header when given -R.
+    dt = indir->child;
     if (S_ISDIR(dt->st.st_mode) && !dt->next && !(flags & FLAG_d)) {
-      dt->extra = 1;
-      listfiles(open(dt->name, 0), dt);
+      listfiles(open(dt->name, 0), TT.singledir = dt);
 
       return;
     }
   } else {
     // Read directory contents. We dup() the fd because this will close it.
+    // This reads/saves contents to display later, except for in "ls -1f" mode.
     indir->data = dup(dirfd);
     dirtree_recurse(indir, filter, DIRTREE_SYMFOLLOW*!!(flags&FLAG_L));
   }
@@ -329,7 +311,7 @@
   }
 
   // Label directory if not top of tree, or if -R
-  if (indir->parent && (!indir->extra || (flags & FLAG_R)))
+  if (indir->parent && (TT.singledir!=indir || (flags&FLAG_R)))
   {
     char *path = dirtree_path(indir, 0);
 
@@ -350,7 +332,7 @@
       blocks += sort[ul]->st.st_blocks;
     }
     totpad = totals[1]+!!totals[1]+totals[6]+!!totals[6]+totals[7]+!!totals[7];
-    if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g|FLAG_s) && indir->parent)
+    if ((flags&(FLAG_l|FLAG_o|FLAG_n|FLAG_g|FLAG_s)) && indir->parent)
       xprintf("total %llu\n", blocks);
   }
 
@@ -442,7 +424,8 @@
       printf("%s% *ld %s%s%s%s", perm, totals[2]+1, (long)st->st_nlink,
              usr, upad, grp, grpad);
 
-      if (CFG_LS_Z && (flags & FLAG_Z)) seclabel(sort[next], -(int)totals[7]);
+      if (flags & FLAG_Z)
+        printf("%*s ", -(int)totals[7], (char *)sort[next]->extra);
 
       if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
         printf("% *d,% 4d", totals[5]-4, major(st->st_rdev),minor(st->st_rdev));
@@ -451,7 +434,8 @@
       tm = localtime(&(st->st_mtime));
       strftime(thyme, sizeof(thyme), "%F %H:%M", tm);
       xprintf(" %s ", thyme);
-    } else if (CFG_LS_Z && (flags & FLAG_Z)) seclabel(sort[next], totals[7]);
+    } else if (flags & FLAG_Z)
+      printf("%*s ", (int)totals[7], (char *)sort[next]->extra);
 
     if (flags & FLAG_color) {
       color = color_from_mode(st->st_mode);
@@ -493,11 +477,10 @@
   // Free directory entries, recursing first if necessary.
 
   for (ul = 0; ul<dtlen; free(sort[ul++])) {
-    if ((flags & FLAG_d) || !S_ISDIR(sort[ul]->st.st_mode)
-      || !dirtree_notdotdot(sort[ul])) continue;
+    if ((flags & FLAG_d) || !S_ISDIR(sort[ul]->st.st_mode)) continue;
 
     // Recurse into dirs if at top of the tree or given -R
-    if (!indir->parent || (flags & FLAG_R))
+    if (!indir->parent || ((flags&FLAG_R) && !dirtree_notdotdot(sort[ul])))
       listfiles(openat(dirfd, sort[ul]->name, 0), sort[ul]);
   }
   free(sort);