[newlib-cygwin] Cygwin: Add lsattr and chattr tools

Corinna Vinschen corinna@sourceware.org
Wed Dec 26 09:42:00 GMT 2018


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=0d4b39d37b966eccc27ff383f6453797f576859d

commit 0d4b39d37b966eccc27ff383f6453797f576859d
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Tue Dec 25 23:39:11 2018 +0100

    Cygwin: Add lsattr and chattr tools
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/utils/Makefile.in |   2 +-
 winsup/utils/chattr.c    | 362 +++++++++++++++++++++++++++++++++++++++++++++++
 winsup/utils/lsattr.c    | 289 +++++++++++++++++++++++++++++++++++++
 3 files changed, 652 insertions(+), 1 deletion(-)

diff --git a/winsup/utils/Makefile.in b/winsup/utils/Makefile.in
index be525d0..b64f457 100644
--- a/winsup/utils/Makefile.in
+++ b/winsup/utils/Makefile.in
@@ -54,7 +54,7 @@ MINGW_CXX      := @MINGW_CXX@
 
 # List all binaries to be linked in Cygwin mode.  Each binary on this list
 # must have a corresponding .o of the same name.
-CYGWIN_BINS := ${addsuffix .exe,cygpath gencat getconf getfacl ldd locale kill minidumper mkgroup \
+CYGWIN_BINS := ${addsuffix .exe,chattr cygpath gencat getconf getfacl ldd locale lsattr kill minidumper mkgroup \
         mkpasswd mount passwd pldd ps regtool setfacl setmetamode ssp tzset umount}
 
 # List all binaries to be linked in MinGW mode.  Each binary on this list
diff --git a/winsup/utils/chattr.c b/winsup/utils/chattr.c
new file mode 100644
index 0000000..c21d4b7
--- /dev/null
+++ b/winsup/utils/chattr.c
@@ -0,0 +1,362 @@
+/* chattr.c
+
+   Written by Corinna Vinschen <vinschen@redhat.com>
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <cygwin/fs.h>
+#include <cygwin/version.h>
+
+int Ropt, Vopt, fopt;
+uint64_t add, del, set;
+
+struct option longopts[] = {
+  { "recursive", no_argument, NULL, 'R' },
+  { "verbose", no_argument, NULL, 'V' },
+  { "force", no_argument, NULL, 'f' },
+  { "help", no_argument, NULL, 'h' },
+  { "version", no_argument, NULL, 'v' },
+  { NULL, no_argument, NULL, 0}
+};
+
+const char *opts = "+RVfhv";
+
+struct
+{
+  uint64_t	 flagval;
+  char		 chr;
+  const char	*str;
+} supp_flag[] = {
+  { FS_READONLY_FL,	'r',	"Readonly" },
+  { FS_HIDDEN_FL,	'h',	"Hidden" },
+  { FS_SYSTEM_FL,	's',	"System" },
+  { FS_ARCHIVE_FL,	'a',	"Archive" },
+  { FS_TEMP_FL,		't',	"Temporary" },
+  { FS_SPARSE_FL,	'S',	"Sparse" },
+  { FS_REPARSE_FL,	'r',	NULL },
+  { FS_COMPRESSED_FL,	'c',	"Compressed" },
+  { FS_OFFLINE_FL,	'o',	NULL },
+  { FS_NOTINDEXED_FL,	'n',	"Notindexed" },
+  { FS_ENCRYPT_FL,	'e',	"Encrypted" },
+  { FS_CASESENS_FL,	'C',	"Casesensitive" },
+  { 0,			'\0',	NULL },
+};
+const char *supp_list = "rhsatSrconeC";
+
+void
+print_flags (uint64_t flags)
+{
+  int i;
+
+  for (i = 0; supp_flag[i].flagval; ++i)
+    fputc ((flags & supp_flag[i].flagval) ? supp_flag[i].chr : '-', stdout);
+}
+
+int
+get_flags (const char *opt)
+{
+  const char *p = opt, *sl;
+  uint64_t *mode;
+  ptrdiff_t idx;
+
+  switch (*p)
+    {
+    case '+':
+      mode = &add;
+      break;
+    case '-':
+      mode = &del;
+      break;
+    case '=':
+      mode = &set;
+      break;
+    default:
+      return 1;
+    }
+  while (*++p)
+    {
+      sl = strchr (supp_list, *p);
+      if (!sl)
+	return 1;
+      idx = sl - supp_list;
+      if (!supp_flag[idx].str)
+	return 1;
+      *mode |= supp_flag[idx].flagval;
+    }
+  return 0;
+}
+
+int
+sanity_check ()
+{
+  int ret = -1;
+  if (!set && !add && !del)
+    fprintf (stderr, "%s: Must use at least one of =, + or -\n",
+	     program_invocation_short_name);
+  else if (set && (add | del))
+    fprintf (stderr, "%s: = is incompatible with + and -\n",
+	     program_invocation_short_name);
+  else if ((add & del) != 0)
+    fprintf (stderr, "%s: Can't both set and unset same flag.\n",
+	     program_invocation_short_name);
+  else
+    ret = 0;
+  return ret;
+}
+
+int
+chattr (const char *path)
+{
+  int fd;
+  uint64_t flags, newflags;
+
+  fd = open (path, O_RDONLY);
+  if (fd < 0)
+    {
+      fprintf (stderr, "%s: %s while trying to open %s\n",
+	       program_invocation_short_name, strerror (errno), path);
+      return 1;
+    }
+  if (ioctl (fd, FS_IOC_GETFLAGS, &flags))
+    {
+      close (fd);
+      fprintf (stderr, "%s: %s while trying to fetch flags from %s\n",
+	       program_invocation_short_name, strerror (errno), path);
+      return 1;
+    }
+  if (set)
+    newflags = set;
+  else
+    {
+      newflags = flags;
+      newflags |= add;
+      newflags &= ~del;
+    }
+  if (newflags != flags)
+    {
+      if (Vopt)
+	{
+	  printf ("Flags of %s set as ", path);
+	  print_flags (newflags);
+	  fputc ('\n', stdout);
+	}
+      if (ioctl (fd, FS_IOC_SETFLAGS, &newflags))
+	{
+	  close (fd);
+	  fprintf (stderr, "%s: %s while trying to set flags on %s\n",
+		   program_invocation_short_name, strerror (errno), path);
+	  return 1;
+	}
+    }
+  close (fd);
+  return 0;
+}
+
+int
+chattr_dir (const char *path)
+{
+  DIR *dir;
+  struct dirent *de;
+  char *subpath = (char *) malloc (strlen (path) + 1 + NAME_MAX + 1);
+  char *comp;
+
+  dir = opendir (path);
+  if (!dir)
+    {
+      free (subpath);
+      return 1;
+    }
+  comp = stpcpy (subpath, path);
+  if (comp[-1] != '/')
+    *comp++ = '/';
+  while ((de = readdir (dir)))
+    {
+      struct stat st;
+
+      if (strcmp (de->d_name, ".") == 0 || strcmp (de->d_name, "..") == 0)
+	continue;
+
+      stpcpy (comp, de->d_name);
+      if (lstat (subpath, &st) != 0)
+	fprintf (stderr, "%s: %s while trying to stat %s\n",
+		 program_invocation_short_name, strerror (errno),
+		 subpath);
+      else
+	{
+	  if (S_ISREG (st.st_mode) || S_ISDIR (st.st_mode))
+	    chattr (subpath);
+	  if (S_ISDIR (st.st_mode) && Ropt)
+	    chattr_dir (subpath);
+	}
+    }
+  free (subpath);
+  return 0;
+}
+
+static void
+print_version ()
+{
+  printf ("%s (cygwin) %d.%d.%d\n"
+	  "Get POSIX ACL information\n"
+	  "Copyright (C) 2018 - %s Cygwin Authors\n"
+	  "This is free software; see the source for copying conditions.  "
+	  "There is NO\n"
+	  "warranty; not even for MERCHANTABILITY or FITNESS FOR A "
+	  "PARTICULAR PURPOSE.\n",
+	  program_invocation_short_name,
+	  CYGWIN_VERSION_DLL_MAJOR / 1000,
+	  CYGWIN_VERSION_DLL_MAJOR % 1000,
+	  CYGWIN_VERSION_DLL_MINOR,
+	  strrchr (__DATE__, ' ') + 1);
+}
+
+static void
+usage (FILE *stream)
+{
+  fprintf (stream, "Usage: %s [-RVfhv] [+-=mode]... [file]...\n",
+	   program_invocation_short_name);
+  if (stream == stderr)
+    fprintf (stream, "Try '%s --help' for more information\n",
+	     program_invocation_short_name);
+  if (stream == stdout)
+    fprintf (stream, "\n"
+      "Change file attributes\n"
+      "\n"
+      "  -R, --recursive     recursively list attributes of directories and their \n"
+      "                      contents\n"
+      "  -V, --verbose       Be verbose during operation\n"
+      "  -f, --force         suppress error messages\n"
+      "  -h, --help          this help text\n"
+      "  -v, --version       display the program version\n"
+      "\n"
+      "The format of 'mode' is {+-=}[acCehnrsSt]\n"
+      "\n"
+      "The  operator '+' causes the selected attributes to be added to the\n"
+      "existing attributes of the files; '-' causes them to be removed; and\n"
+      "'=' causes them to be the only attributes that the files have.\n"
+      "\n"
+      "Supported attributes:\n"
+      "\n"
+      "  'r', 'Readonly':     file is read-only\n"
+      "  'h', 'Hidden':        file or directory is hidden\n"
+      "  's', 'System':        file or directory that the operating system uses\n"
+      "  'a', 'Archive':       file or directory has the archive marker set\n"
+      "  't', 'Temporary':     file is being used for temporary storage\n"
+      "  'S', 'Sparse':        file is sparse\n"
+      "  'c', 'Compressed':    file or directory is compressed\n"
+      "  'n', 'Notindexed':    file or directory is not to be indexed by the\n"
+      "                        content indexing service\n"
+      "  'e', 'Encrypted':     file is encrypted\n"
+      "  'C', 'Casesensitive': directory is handled case sensitive\n"
+      "                        (Windows 10 1803 or later, local NTFS only,\n"
+      "                         WSL must be installed)\n");
+}
+
+int
+main (int argc, char **argv)
+{
+  int c, ret = 0;
+  int lastoptind = 0;
+  char *opt;
+
+  opterr = 0;
+  while ((c = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
+    {
+      switch (c)
+	{
+	case 'R':
+	  Ropt = 1;
+	  lastoptind = optind;
+	  break;
+	case 'V':
+	  Vopt = 1;
+	  lastoptind = optind;
+	  break;
+	case 'f':
+	  fopt = 1;
+	  lastoptind = optind;
+	  break;
+	case 'v':
+	  print_version ();
+	  return 0;
+	  break;
+	default:
+	  if (optind > lastoptind)
+	    {
+	      --optind;
+	      goto next;
+	    }
+	  /*FALLTHRU*/
+	case 'h':
+	  usage (c == 'h' ? stdout : stderr);
+	  return 1;
+	}
+    }
+next:
+  while (optind < argc)
+    {
+      if (strcmp (argv[optind], "--") == 0)
+	{
+	  ++optind;
+	  break;
+	}
+      opt = strchr ("+-=", argv[optind][0]);
+      if (!opt)
+	break;
+      if (argv[optind][1] == '\0' || get_flags (argv[optind]))
+	{
+	  usage (stderr);
+	  return 1;
+	}
+      ++optind;
+    }
+  if (sanity_check ())
+    return 1;
+  if (optind > argc - 1)
+    {
+      chattr (".");
+      if (Ropt)
+	chattr_dir (".");
+    }
+  else for (; optind < argc; ++optind)
+    {
+      struct stat st;
+
+      if (lstat (argv[optind], &st) != 0)
+	{
+	  fprintf (stderr, "%s: %s while trying to stat %s\n",
+		   program_invocation_short_name, strerror (errno),
+		   argv[optind]);
+	  ret = 1;
+	}
+      else if (!S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode))
+	{
+	  fprintf (stderr, "%s: %s on %s\n",
+		   program_invocation_short_name, strerror (ENOTSUP),
+		   argv[optind]);
+	  ret = 1;
+	}
+      else
+	{
+	  if (chattr (argv[optind]))
+	    ret = 1;
+	  if (S_ISDIR (st.st_mode) && chattr_dir (argv[optind]))
+	    ret = 1;
+	}
+    }
+  return ret;
+}
diff --git a/winsup/utils/lsattr.c b/winsup/utils/lsattr.c
new file mode 100644
index 0000000..ee72043
--- /dev/null
+++ b/winsup/utils/lsattr.c
@@ -0,0 +1,289 @@
+/* lsattr.c
+
+   Written by Corinna Vinschen <vinschen@redhat.com>
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <cygwin/fs.h>
+#include <cygwin/version.h>
+
+int Ropt, aopt, dopt, lopt, nopt;
+
+struct option longopts[] = {
+  { "recursive", no_argument, NULL, 'R' },
+  { "version", no_argument, NULL, 'V' },
+  { "all", no_argument, NULL, 'a' },
+  { "directory", no_argument, NULL, 'd' },
+  { "help", no_argument, NULL, 'h' },
+  { "long", no_argument, NULL, 'l' },
+  { "no-headers", no_argument, NULL, 'n' },
+  { NULL, no_argument, NULL, 0}
+};
+
+const char *opts = "+RVadhln";
+
+struct
+{
+  uint64_t	 flagval;
+  char		 chr;
+  const char	*str;
+} supp_flag[] = {
+  { FS_READONLY_FL,	'r',	"Readonly" },
+  { FS_HIDDEN_FL,	'h',	"Hidden" },
+  { FS_SYSTEM_FL,	's',	"System" },
+  { FS_ARCHIVE_FL,	'a',	"Archive" },
+  { FS_TEMP_FL,		't',	"Temporary" },
+  { FS_SPARSE_FL,	'S',	"Sparse" },
+  { FS_REPARSE_FL,	'r',	"Reparse" },
+  { FS_COMPRESSED_FL,	'c',	"Compressed" },
+  { FS_OFFLINE_FL,	'o',	"Offline" },
+  { FS_NOTINDEXED_FL,	'n',	"Notindexed" },
+  { FS_ENCRYPT_FL,	'e',	"Encrypted" },
+  { FS_CASESENS_FL,	'C',	"Casesensitive" },
+  { 0,			'\0',	NULL },
+};
+
+void
+print_long (const char *path, uint64_t flags)
+{
+  int i;
+  int first = 1;
+
+  printf("%-28s ", path);
+  for (i = 0; supp_flag[i].flagval; ++i)
+    if (flags & supp_flag[i].flagval)
+      {
+	if (!first)
+	  fputs (", ", stdout);
+	first = 0;
+	fputs (supp_flag[i].str, stdout);
+      }
+  if (first)
+    fputs ("---", stdout);
+  fputc ('\n', stdout);
+}
+
+void
+print_short (const char *path, uint64_t flags)
+{
+  int i;
+
+  for (i = 0; supp_flag[i].flagval; ++i)
+    fputc ((flags & supp_flag[i].flagval) ? supp_flag[i].chr : '-', stdout);
+  printf(" %s\n", path);
+}
+
+int
+lsattr (const char *path)
+{
+  int fd;
+  uint64_t flags;
+
+  fd = open (path, O_RDONLY);
+  if (fd < 0)
+    {
+      fprintf (stderr, "%s: %s while trying to open %s\n",
+	       program_invocation_short_name, strerror (errno),
+	       path);
+      return 1;
+    }
+  if (ioctl (fd, FS_IOC_GETFLAGS, &flags))
+    {
+      close (fd);
+      fprintf (stderr, "%s: %s while trying to fetch flags from %s\n",
+	       program_invocation_short_name, strerror (errno),
+	       path);
+      return 1;
+    }
+  close (fd);
+  if (lopt)
+    print_long (path, flags);
+  else
+    print_short (path, flags);
+  return 0;
+}
+
+int
+lsattr_dir (const char *path)
+{
+  DIR *dir;
+  struct dirent *de;
+  char *subpath = (char *) malloc (strlen (path) + 1 + NAME_MAX + 1);
+  char *comp;
+
+  dir = opendir (path);
+  if (!dir)
+    {
+      free (subpath);
+      return 1;
+    }
+  comp = stpcpy (subpath, path);
+  if (comp[-1] != '/')
+    *comp++ = '/';
+  while ((de = readdir (dir)))
+    {
+      struct stat st;
+
+      stpcpy (comp, de->d_name);
+      if (lstat (subpath, &st) != 0)
+	fprintf (stderr, "%s: %s while trying to stat %s\n",
+		 program_invocation_short_name, strerror (errno),
+		 subpath);
+      else if (de->d_name[0] != '.' || aopt)
+	{
+	  if (S_ISREG (st.st_mode) || S_ISDIR (st.st_mode))
+	    lsattr (subpath);
+	  if (S_ISDIR (st.st_mode) && Ropt
+	      && strcmp (de->d_name, ".") != 0
+		  && strcmp (de->d_name, "..") != 0)
+	    {
+	      if (!nopt)
+		printf ("\n%s:\n", path);
+	      lsattr_dir (subpath);
+	      if (!nopt)
+		fputc ('\n', stdout);
+	    }
+	}
+    }
+  free (subpath);
+  return 0;
+}
+
+static void
+print_version ()
+{
+  printf ("%s (cygwin) %d.%d.%d\n"
+	  "Get POSIX ACL information\n"
+	  "Copyright (C) 2018 - %s Cygwin Authors\n"
+	  "This is free software; see the source for copying conditions.  "
+	  "There is NO\n"
+	  "warranty; not even for MERCHANTABILITY or FITNESS FOR A "
+	  "PARTICULAR PURPOSE.\n",
+	  program_invocation_short_name,
+	  CYGWIN_VERSION_DLL_MAJOR / 1000,
+	  CYGWIN_VERSION_DLL_MAJOR % 1000,
+	  CYGWIN_VERSION_DLL_MINOR,
+	  strrchr (__DATE__, ' ') + 1);
+}
+
+static void
+usage (FILE *stream)
+{
+  fprintf (stream, "Usage: %s [-RVadhln] [file]...\n",
+	   program_invocation_short_name);
+  if (stream == stderr)
+    fprintf (stream, "Try '%s --help' for more information\n",
+	     program_invocation_short_name);
+  if (stream == stdout)
+    fprintf (stream, "\n"
+      "List file attributes\n"
+      "\n"
+      "  -R, --recursive     recursively list attributes of directories and their \n"
+      "                      contents\n"
+      "  -V, --version       display the program version\n"
+      "  -a, --all           list all files in directories, including files that\n"
+      "                      start with '.'\n"
+      "  -d, --directory     list directories like other files, rather than listing\n"
+      "                      their contents.\n"
+      "  -l, --long          print options using long names instead of single\n"
+      "                      character abbreviations\n"
+      "  -n, --no-headers    don't print directory headers when recursing\n"
+      "  -h, --help          this help text\n"
+      "\n"
+      "Supported attributes:\n"
+      "\n"
+      "  'r', 'Readonly':      file is read-only, directory is system-marked\n"
+      "  'h', 'Hidden':        file or directory is hidden\n"
+      "  's', 'System':        file or directory that the operating system uses\n"
+      "  'a', 'Archive':       file or directory has the archive marker set\n"
+      "  't', 'Temporary':     file is being used for temporary storage\n"
+      "  'S', 'Sparse':        file is sparse\n"
+      "  'r', 'Reparse':       file or directory that has a reparse point\n"
+      "  'c', 'Compressed':    file or directory is compressed\n"
+      "  'o', 'Offline':       the data of a file is moved to offline storage\n"
+      "  'n', 'Notindexed':    file or directory is not to be indexed by the\n"
+      "                        content indexing service\n"
+      "  'e', 'Encrypted':     file is encrypted\n"
+      "  'C', 'Casesensitive': directory is handled case sensitive\n"
+      "                        (Windows 10 1803 or later, local NTFS only,\n"
+      "                         WSL must be installed)\n");
+}
+
+int
+main (int argc, char **argv)
+{
+  int c, ret = 0;
+
+  opterr = 0;
+  while ((c = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
+    {
+      switch (c)
+	{
+	case 'R':
+	  Ropt = 1;
+	  break;
+	case 'V':
+	  print_version ();
+	  return 0;
+	case 'a':
+	  aopt = 1;
+	  break;
+	case 'd':
+	  dopt = 1;
+	  break;
+	case 'l':
+	  lopt = 1;
+	  break;
+	case 'n':
+	  nopt = 1;
+	  break;
+	case 'h':
+	default:
+	  usage (c == 'h' ? stdout : stderr);
+	  return 1;
+	}
+    }
+  if (optind > argc - 1)
+    lsattr_dir (".");
+  else for (; optind < argc; ++optind)
+    {
+      struct stat st;
+
+      if (lstat (argv[optind], &st) != 0)
+	{
+	  fprintf (stderr, "%s: %s while trying to stat %s\n",
+		   program_invocation_short_name, strerror (errno),
+		   argv[optind]);
+	  ret = 1;
+	}
+      else if (!S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode))
+	{
+	  fprintf (stderr, "%s: %s on %s\n",
+		   program_invocation_short_name, strerror (ENOTSUP),
+		   argv[optind]);
+	  ret = 1;
+	}
+      else if (S_ISDIR (st.st_mode) && !dopt)
+	{
+	  if (lsattr_dir (argv[optind]))
+	    ret = 1;
+	}
+      else if (lsattr (argv[optind]))
+      	ret = 1;
+    }
+  return ret;
+}



More information about the Cygwin-cvs mailing list