busybox/coreutils/chmod.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini chmod implementation for busybox
   4 *
   5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
   6 *
   7 * Reworked by (C) 2002 Vladimir Oleynik <dzo@simtreas.ru>
   8 *  to correctly parse '-rwxgoa'
   9 *
  10 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  11 */
  12
  13/* BB_AUDIT SUSv3 compliant */
  14/* BB_AUDIT GNU defects - unsupported long options. */
  15/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
  16
  17#include "libbb.h"
  18
  19/* This is a NOEXEC applet. Be very careful! */
  20
  21
  22#define OPT_RECURSE (option_mask32 & 1)
  23#define OPT_VERBOSE (IF_DESKTOP(option_mask32 & 2) IF_NOT_DESKTOP(0))
  24#define OPT_CHANGED (IF_DESKTOP(option_mask32 & 4) IF_NOT_DESKTOP(0))
  25#define OPT_QUIET   (IF_DESKTOP(option_mask32 & 8) IF_NOT_DESKTOP(0))
  26#define OPT_STR     "R" IF_DESKTOP("vcf")
  27
  28/* coreutils:
  29 * chmod never changes the permissions of symbolic links; the chmod
  30 * system call cannot change their permissions. This is not a problem
  31 * since the permissions of symbolic links are never used.
  32 * However, for each symbolic link listed on the command line, chmod changes
  33 * the permissions of the pointed-to file. In contrast, chmod ignores
  34 * symbolic links encountered during recursive directory traversals.
  35 */
  36
  37static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void* param, int depth)
  38{
  39        mode_t newmode;
  40
  41        /* match coreutils behavior */
  42        if (depth == 0) {
  43                /* statbuf holds lstat result, but we need stat (follow link) */
  44                if (stat(fileName, statbuf))
  45                        goto err;
  46        } else { /* depth > 0: skip links */
  47                if (S_ISLNK(statbuf->st_mode))
  48                        return TRUE;
  49        }
  50        newmode = statbuf->st_mode;
  51
  52        if (!bb_parse_mode((char *)param, &newmode))
  53                bb_error_msg_and_die("invalid mode: %s", (char *)param);
  54
  55        if (chmod(fileName, newmode) == 0) {
  56                if (OPT_VERBOSE
  57                 || (OPT_CHANGED && statbuf->st_mode != newmode)
  58                ) {
  59                        printf("mode of '%s' changed to %04o (%s)\n", fileName,
  60                                newmode & 07777, bb_mode_string(newmode)+1);
  61                }
  62                return TRUE;
  63        }
  64 err:
  65        if (!OPT_QUIET)
  66                bb_simple_perror_msg(fileName);
  67        return FALSE;
  68}
  69
  70int chmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  71int chmod_main(int argc UNUSED_PARAM, char **argv)
  72{
  73        int retval = EXIT_SUCCESS;
  74        char *arg, **argp;
  75        char *smode;
  76
  77        /* Convert first encountered -r into ar, -w into aw etc
  78         * so that getopt would not eat it */
  79        argp = argv;
  80        while ((arg = *++argp)) {
  81                /* Mode spec must be the first arg (sans -R etc) */
  82                /* (protect against mishandling e.g. "chmod 644 -r") */
  83                if (arg[0] != '-') {
  84                        arg = NULL;
  85                        break;
  86                }
  87                /* An option. Not a -- or valid option? */
  88                if (arg[1] && !strchr("-"OPT_STR, arg[1])) {
  89                        arg[0] = 'a';
  90                        break;
  91                }
  92        }
  93
  94        /* Parse options */
  95        opt_complementary = "-2";
  96        getopt32(argv, ("-"OPT_STR) + 1); /* Reuse string */
  97        argv += optind;
  98
  99        /* Restore option-like mode if needed */
 100        if (arg) arg[0] = '-';
 101
 102        /* Ok, ready to do the deed now */
 103        smode = *argv++;
 104        do {
 105                if (!recursive_action(*argv,
 106                        OPT_RECURSE,    // recurse
 107                        fileAction,     // file action
 108                        fileAction,     // dir action
 109                        smode,          // user data
 110                        0)              // depth
 111                ) {
 112                        retval = EXIT_FAILURE;
 113                }
 114        } while (*++argv);
 115
 116        return retval;
 117}
 118
 119/*
 120Security: chmod is too important and too subtle.
 121This is a test script (busybox chmod versus coreutils).
 122Run it in empty directory.
 123
 124#!/bin/sh
 125t1="/tmp/busybox chmod"
 126t2="/usr/bin/chmod"
 127create() {
 128    rm -rf $1; mkdir $1
 129    (
 130    cd $1 || exit 1
 131    mkdir dir
 132    >up
 133    >file
 134    >dir/file
 135    ln -s dir linkdir
 136    ln -s file linkfile
 137    ln -s ../up dir/up
 138    )
 139}
 140tst() {
 141    (cd test1; $t1 $1)
 142    (cd test2; $t2 $1)
 143    (cd test1; ls -lR) >out1
 144    (cd test2; ls -lR) >out2
 145    echo "chmod $1" >out.diff
 146    if ! diff -u out1 out2 >>out.diff; then exit 1; fi
 147    rm out.diff
 148}
 149echo "If script produced 'out.diff' file, then at least one testcase failed"
 150create test1; create test2
 151tst "a+w file"
 152tst "a-w dir"
 153tst "a+w linkfile"
 154tst "a-w linkdir"
 155tst "-R a+w file"
 156tst "-R a-w dir"
 157tst "-R a+w linkfile"
 158tst "-R a-w linkdir"
 159tst "a-r,a+x linkfile"
 160*/
 161