diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2018-12-26 01:39:11 +0300 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2018-12-26 11:24:33 +0300 |
commit | 0d4b39d37b966eccc27ff383f6453797f576859d (patch) | |
tree | dd4664a93ce79bc8168ea9e7a836a014340d0ece /winsup/utils/chattr.c | |
parent | af4a65a26d703cf150cd6d75336c19543aa014d8 (diff) |
Cygwin: Add lsattr and chattr tools
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
Diffstat (limited to 'winsup/utils/chattr.c')
-rw-r--r-- | winsup/utils/chattr.c | 362 |
1 files changed, 362 insertions, 0 deletions
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; +} |