* src/env.c: add -M/--umask parameter. * doc/coreutils.texi: add -M/--umask documentation. * tests/env/umask.sh: add -M/--umask tests. --- doc/coreutils.texi | 16 ++++++++++++++++ src/env.c | 42 +++++++++++++++++++++++++++++++++++++++++- tests/env/umask.sh | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 tests/env/umask.sh
diff --git a/doc/coreutils.texi b/doc/coreutils.texi index b552cc105..9e2c0f4b0 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -17243,6 +17243,22 @@ chroot /chroot env --chdir=/srv true env --chdir=/build FOO=bar timeout 5 true @end example +@item -M @var{mask} +@itemx --umask=@var{mask} +@opindex -M +@opindex --umask +Set the umask used when creating new files or directories to @var{mask} +before invoking @var{command}. This differs from the shell built-in +@command{umask} in that it starts @var{command} as a subprocess rather than +altering the shell's own umask; this allows it to be chained with other +commands that run commands in a different context. For example: + +@example +# Run 'touch' with 0777 as its umask, FOO=bar in its +# environment, and a time limit of five seconds. +env --umask=0777 FOO=bar timeout 5 touch a-file +@end example + @item --default-signal[=@var{sig}] Unblock and reset signal @var{sig} to its default signal handler. Without @var{sig} all known signals are unblocked and reset to their defaults. diff --git a/src/env.c b/src/env.c index c8161356c..b3d6c01bc 100644 --- a/src/env.c +++ b/src/env.c @@ -18,7 +18,9 @@ #include <config.h> #include <stdio.h> +#include <stdlib.h> #include <sys/types.h> +#include <sys/stat.h> #include <getopt.h> #include <c-ctype.h> #include <signal.h> @@ -73,7 +75,7 @@ static bool sig_mask_changed; /* Whether to list non default handling. */ static bool report_signal_handling; -static char const shortopts[] = "+C:iS:u:v0 \t"; +static char const shortopts[] = "+C:iS:M:u:v0 \t"; /* For long options that have no equivalent short option, use a non-character as a pseudo short option, starting with CHAR_MAX + 1. */ @@ -89,6 +91,7 @@ static struct option const longopts[] = { {"ignore-environment", no_argument, NULL, 'i'}, {"null", no_argument, NULL, '0'}, + {"umask", required_argument, NULL, 'M'}, {"unset", required_argument, NULL, 'u'}, {"chdir", required_argument, NULL, 'C'}, {"default-signal", optional_argument, NULL, DEFAULT_SIGNAL_OPTION}, @@ -129,6 +132,9 @@ Set each NAME to VALUE in the environment and run COMMAND.\n\ fputs (_("\ -S, --split-string=S process and split S into separate arguments;\n\ used to pass multiple arguments on shebang lines\n\ +"), stdout); + fputs (_("\ + -M, --umask=MASK set MASK as umask\n\ "), stdout); fputs (_("\ --block-signal[=SIG] block delivery of SIG signal(s) to COMMAND\n\ @@ -622,6 +628,23 @@ parse_signal_action_params (const char* optarg, bool set_default) free (optarg_writable); } +static mode_t +parse_umask_param (char const * optarg) +{ + char * end = NULL; + + errno = 0; + long int res = strtol (optarg, &end, 8); + + if (errno || *end) + die (EXIT_CANCELED, errno, _("unable to parse umask: %s"), optarg); + + if (res > 07777 || res < 0) + die (EXIT_CANCELED, 0, _("invalid umask: %s"), optarg); + + return (mode_t)res; +} + static void reset_signal_handlers (void) { @@ -801,6 +824,7 @@ main (int argc, char **argv) bool ignore_environment = false; bool opt_nul_terminate_output = false; char const *newdir = NULL; + mode_t program_umask = -1; initialize_main (&argc, &argv); set_program_name (argv[0]); @@ -820,6 +844,9 @@ main (int argc, char **argv) case 'i': ignore_environment = true; break; + case 'M': + program_umask = parse_umask_param (optarg); + break; case 'u': append_unset_var (optarg); break; @@ -902,6 +929,12 @@ main (int argc, char **argv) usage (EXIT_CANCELED); } + if (program_umask >= 0 && ! program_specified) + { + error (0, 0, _("must specify command with --umask (-M)")); + usage (EXIT_CANCELED); + } + if (newdir && ! program_specified) { error (0, 0, _("must specify command with --chdir (-C)")); @@ -924,6 +957,13 @@ main (int argc, char **argv) if (report_signal_handling) list_signal_handling (); + if (program_umask) + { + devmsg ("umask: %o\n", program_umask); + + umask (program_umask); + } + if (newdir) { devmsg ("chdir: %s\n", quoteaf (newdir)); diff --git a/tests/env/umask.sh b/tests/env/umask.sh new file mode 100644 index 000000000..b2e48fff2 --- /dev/null +++ b/tests/env/umask.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# Test env's -M/--umask option. + +# Copyright (C) 2019 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ env + +# test getopt setup +env -M 0777 true || fail=1 +env --umask 0777 true || fail=2 + +# remove read permission +env -M 0777 touch out || fail=3 +test -r out && fail=4 + +# test param handling +env -M 0777 && fail=5 +env -M a touch out && fail=6 +env -M 9 touch out && fail=7 +env -M 17777 touch out && fail=8 +env -M -1 touch out && fail=9 + +Exit $fail -- a