summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2018-12-25 23:39:11 +0100
committerCorinna Vinschen <corinna@vinschen.de>2018-12-26 09:24:33 +0100
commit0d4b39d37b966eccc27ff383f6453797f576859d (patch)
treedd4664a93ce79bc8168ea9e7a836a014340d0ece
parentaf4a65a26d703cf150cd6d75336c19543aa014d8 (diff)
downloadcygnal-0d4b39d37b966eccc27ff383f6453797f576859d.tar.gz
cygnal-0d4b39d37b966eccc27ff383f6453797f576859d.tar.bz2
cygnal-0d4b39d37b966eccc27ff383f6453797f576859d.zip
Cygwin: Add lsattr and chattr tools
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
-rw-r--r--winsup/utils/Makefile.in2
-rw-r--r--winsup/utils/chattr.c362
-rw-r--r--winsup/utils/lsattr.c289
3 files changed, 652 insertions, 1 deletions
diff --git a/winsup/utils/Makefile.in b/winsup/utils/Makefile.in
index be525d07f..b64f457e7 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 000000000..c21d4b7a5
--- /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 000000000..ee72043f0
--- /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;
+}