| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
|
|
| #include <config.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <getopt.h> |
|
|
| #include "system.h" |
| #include "chown.h" |
| #include "chown-core.h" |
| #include "fts_.h" |
| #include "quote.h" |
| #include "root-dev-ino.h" |
| #include "userspec.h" |
|
|
| |
| #define PROGRAM_NAME (chown_mode == CHOWN_CHOWN ? "chown" : "chgrp") |
|
|
| #define AUTHORS \ |
| proper_name ("David MacKenzie"), \ |
| proper_name ("Jim Meyering") |
|
|
| |
| |
| static char *reference_file; |
|
|
| |
| |
| enum |
| { |
| DEREFERENCE_OPTION = CHAR_MAX + 1, |
| FROM_OPTION, |
| NO_PRESERVE_ROOT, |
| PRESERVE_ROOT, |
| REFERENCE_FILE_OPTION |
| }; |
|
|
| static struct option const long_options[] = |
| { |
| {"recursive", no_argument, nullptr, 'R'}, |
| {"changes", no_argument, nullptr, 'c'}, |
| {"dereference", no_argument, nullptr, DEREFERENCE_OPTION}, |
| {"from", required_argument, nullptr, FROM_OPTION}, |
| {"no-dereference", no_argument, nullptr, 'h'}, |
| {"no-preserve-root", no_argument, nullptr, NO_PRESERVE_ROOT}, |
| {"preserve-root", no_argument, nullptr, PRESERVE_ROOT}, |
| {"quiet", no_argument, nullptr, 'f'}, |
| {"silent", no_argument, nullptr, 'f'}, |
| {"reference", required_argument, nullptr, REFERENCE_FILE_OPTION}, |
| {"verbose", no_argument, nullptr, 'v'}, |
| {GETOPT_HELP_OPTION_DECL}, |
| {GETOPT_VERSION_OPTION_DECL}, |
| {nullptr, 0, nullptr, 0} |
| }; |
|
|
| void |
| usage (int status) |
| { |
| if (status != EXIT_SUCCESS) |
| emit_try_help (); |
| else |
| { |
| printf (_("\ |
| Usage: %s [OPTION]... %s FILE...\n\ |
| or: %s [OPTION]... --reference=RFILE FILE...\n\ |
| "), |
| program_name, |
| chown_mode == CHOWN_CHOWN ? _("[OWNER][:[GROUP]]") : _("GROUP"), |
| program_name); |
| if (chown_mode == CHOWN_CHOWN) |
| fputs (_("\ |
| Change the owner and/or group of each FILE to OWNER and/or GROUP.\n\ |
| With --reference, change the owner and group of each FILE to those of RFILE.\n\ |
| \n\ |
| "), stdout); |
| else |
| fputs (_("\ |
| Change the group of each FILE to GROUP.\n\ |
| With --reference, change the group of each FILE to that of RFILE.\n\ |
| \n\ |
| "), stdout); |
| fputs (_("\ |
| -c, --changes like verbose but report only when a change is made\n\ |
| -f, --silent, --quiet suppress most error messages\n\ |
| -v, --verbose output a diagnostic for every file processed\n\ |
| "), stdout); |
| fputs (_("\ |
| --dereference affect the referent of each symbolic link (this is\n\ |
| the default), rather than the symbolic link itself\n\ |
| -h, --no-dereference affect symbolic links instead of any referenced file\n\ |
| "), stdout); |
| fputs (_("\ |
| (useful only on systems that can change the\n\ |
| ownership of a symlink)\n\ |
| "), stdout); |
| fputs (_("\ |
| --from=CURRENT_OWNER:CURRENT_GROUP\n\ |
| change the ownership of each file only if\n\ |
| its current owner and/or group match those specified\n\ |
| here. Either may be omitted, in which case a match\n\ |
| is not required for the omitted attribute\n\ |
| "), stdout); |
| fputs (_("\ |
| --no-preserve-root do not treat '/' specially (the default)\n\ |
| --preserve-root fail to operate recursively on '/'\n\ |
| "), stdout); |
| fputs (_("\ |
| --reference=RFILE use RFILE's ownership rather than specifying values.\n\ |
| RFILE is always dereferenced if a symbolic link.\n\ |
| "), stdout); |
| fputs (_("\ |
| -R, --recursive operate on files and directories recursively\n\ |
| "), stdout); |
| emit_symlink_recurse_options ("-P"); |
| fputs (HELP_OPTION_DESCRIPTION, stdout); |
| fputs (VERSION_OPTION_DESCRIPTION, stdout); |
| if (chown_mode == CHOWN_CHOWN) |
| fputs (_("\ |
| \n\ |
| Owner is unchanged if missing. Group is unchanged if missing, but changed\n\ |
| to login group if implied by a ':' following a symbolic OWNER.\n\ |
| OWNER and GROUP may be numeric as well as symbolic.\n\ |
| "), stdout); |
|
|
| if (chown_mode == CHOWN_CHOWN) |
| printf (_("\ |
| \n\ |
| Examples:\n\ |
| %s root /u Change the owner of /u to \"root\".\n\ |
| %s root:staff /u Likewise, but also change its group to \"staff\".\n\ |
| %s -hR root /u Change the owner of /u and subfiles to \"root\".\n\ |
| "), |
| program_name, program_name, program_name); |
| else |
| printf (_("\ |
| \n\ |
| Examples:\n\ |
| %s staff /u Change the group of /u to \"staff\".\n\ |
| %s -hR staff /u Change the group of /u and subfiles to \"staff\".\n\ |
| "), |
| program_name, program_name); |
| emit_ancillary_info (PROGRAM_NAME); |
| } |
| exit (status); |
| } |
|
|
| int |
| main (int argc, char **argv) |
| { |
| bool preserve_root = false; |
|
|
| uid_t uid = -1; |
| gid_t gid = -1; |
|
|
| |
| |
| uid_t required_uid = -1; |
| gid_t required_gid = -1; |
|
|
| |
| int bit_flags = FTS_PHYSICAL; |
|
|
| |
| |
| int dereference = -1; |
|
|
| struct Chown_option chopt; |
| bool ok; |
| int optc; |
|
|
| initialize_main (&argc, &argv); |
| set_program_name (argv[0]); |
| setlocale (LC_ALL, ""); |
| bindtextdomain (PACKAGE, LOCALEDIR); |
| textdomain (PACKAGE); |
|
|
| atexit (close_stdout); |
|
|
| chopt_init (&chopt); |
|
|
| while ((optc = getopt_long (argc, argv, "HLPRcfhv", long_options, nullptr)) |
| != -1) |
| { |
| switch (optc) |
| { |
| case 'H': |
| bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL; |
| break; |
|
|
| case 'L': |
| bit_flags = FTS_LOGICAL; |
| break; |
|
|
| case 'P': |
| bit_flags = FTS_PHYSICAL; |
| break; |
|
|
| case 'h': |
| dereference = 0; |
| break; |
|
|
| case DEREFERENCE_OPTION: |
| |
| dereference = 1; |
| break; |
|
|
| case NO_PRESERVE_ROOT: |
| preserve_root = false; |
| break; |
|
|
| case PRESERVE_ROOT: |
| preserve_root = true; |
| break; |
|
|
| case REFERENCE_FILE_OPTION: |
| reference_file = optarg; |
| break; |
|
|
| case FROM_OPTION: |
| { |
| bool warn; |
| char const *e = parse_user_spec_warn (optarg, |
| &required_uid, &required_gid, |
| nullptr, nullptr, &warn); |
| if (e) |
| error (warn ? 0 : EXIT_FAILURE, 0, "%s: %s", e, quote (optarg)); |
| break; |
| } |
|
|
| case 'R': |
| chopt.recurse = true; |
| break; |
|
|
| case 'c': |
| chopt.verbosity = V_changes_only; |
| break; |
|
|
| case 'f': |
| chopt.force_silent = true; |
| break; |
|
|
| case 'v': |
| chopt.verbosity = V_high; |
| break; |
|
|
| case_GETOPT_HELP_CHAR; |
| case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); |
| default: |
| usage (EXIT_FAILURE); |
| } |
| } |
|
|
| if (chopt.recurse) |
| { |
| if (bit_flags == FTS_PHYSICAL) |
| { |
| if (dereference == 1) |
| error (EXIT_FAILURE, 0, |
| _("-R --dereference requires either -H or -L")); |
| dereference = 0; |
| } |
| } |
| else |
| { |
| bit_flags = FTS_PHYSICAL; |
| } |
| chopt.affect_symlink_referent = (dereference != 0); |
|
|
| if (argc - optind < (reference_file ? 1 : 2)) |
| { |
| if (argc <= optind) |
| error (0, 0, _("missing operand")); |
| else |
| error (0, 0, _("missing operand after %s"), quote (argv[argc - 1])); |
| usage (EXIT_FAILURE); |
| } |
|
|
| if (reference_file) |
| { |
| struct stat ref_stats; |
| if (stat (reference_file, &ref_stats)) |
| error (EXIT_FAILURE, errno, _("failed to get attributes of %s"), |
| quoteaf (reference_file)); |
|
|
| if (chown_mode == CHOWN_CHOWN) |
| { |
| uid = ref_stats.st_uid; |
| chopt.user_name = uid_to_name (ref_stats.st_uid); |
| } |
| gid = ref_stats.st_gid; |
| chopt.group_name = gid_to_name (ref_stats.st_gid); |
| } |
| else |
| { |
| char *ug = argv[optind]; |
| if (chown_mode == CHOWN_CHGRP) |
| { |
| ug = xmalloc (1 + strlen (argv[optind]) + 1); |
| stpcpy (stpcpy (ug, ":"), argv[optind]); |
| } |
|
|
| bool warn; |
| char const *e = parse_user_spec_warn (ug, &uid, &gid, |
| &chopt.user_name, |
| &chopt.group_name, &warn); |
|
|
| if (ug != argv[optind]) |
| free (ug); |
|
|
| if (e) |
| error (warn ? 0 : EXIT_FAILURE, 0, "%s: %s", e, quote (argv[optind])); |
|
|
| |
| |
| |
| if (chown_mode == CHOWN_CHOWN && !chopt.user_name && chopt.group_name) |
| chopt.user_name = xstrdup (""); |
|
|
| optind++; |
| } |
|
|
| if (chopt.recurse && preserve_root) |
| { |
| static struct dev_ino dev_ino_buf; |
| chopt.root_dev_ino = get_root_dev_ino (&dev_ino_buf); |
| if (chopt.root_dev_ino == nullptr) |
| error (EXIT_FAILURE, errno, _("failed to get attributes of %s"), |
| quoteaf ("/")); |
| } |
|
|
| bit_flags |= FTS_DEFER_STAT; |
| ok = chown_files (argv + optind, bit_flags, |
| uid, gid, |
| required_uid, required_gid, &chopt); |
|
|
| main_exit (ok ? EXIT_SUCCESS : EXIT_FAILURE); |
| } |
|
|