/* lsattr.c Written by Corinna Vinschen 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 #include #include #include #include #include #include #include #include #include #include #include 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_PINNED_FL, 'p', "Pinned" }, { FS_UNPINNED_FL, 'u', "Unpinned" }, { 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 __attribute__ ((__noreturn__)) 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" " 'p', 'Pinned': file is pinned\n" " 'u', 'Unpinned': file is unpinned\n" " 'C', 'Casesensitive': directory is handled case sensitive\n" " (Windows 10 1803 or later, local NTFS only,\n" " WSL must be installed)\n"); exit (stream == stdout ? 0 : 1); } 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); break; } } 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; }