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 source tree.
  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//usage:#define chmod_trivial_usage
  18//usage:       "[-R"IF_DESKTOP("cvf")"] MODE[,MODE]... FILE..."
  19//usage:#define chmod_full_usage "\n\n"
  20//usage:       "Each MODE is one or more of the letters ugoa, one of the\n"
  21//usage:       "symbols +-= and one or more of the letters rwxst\n"
  22//usage:     "\n        -R      Recurse"
  23//usage:        IF_DESKTOP(
  24//usage:     "\n        -c      List changed files"
  25//usage:     "\n        -v      List all files"
  26//usage:     "\n        -f      Hide errors"
  27//usage:        )
  28//usage:
  29//usage:#define chmod_example_usage
  30//usage:       "$ ls -l /tmp/foo\n"
  31//usage:       "-rw-rw-r--    1 root     root            0 Apr 12 18:25 /tmp/foo\n"
  32//usage:       "$ chmod u+x /tmp/foo\n"
  33//usage:       "$ ls -l /tmp/foo\n"
  34//usage:       "-rwxrw-r--    1 root     root            0 Apr 12 18:25 /tmp/foo*\n"
  35//usage:       "$ chmod 444 /tmp/foo\n"
  36//usage:       "$ ls -l /tmp/foo\n"
  37//usage:       "-r--r--r--    1 root     root            0 Apr 12 18:25 /tmp/foo\n"
  38
  39#include "libbb.h"
  40
  41/* This is a NOEXEC applet. Be very careful! */
  42
  43
  44#define OPT_RECURSE (option_mask32 & 1)
  45#define OPT_VERBOSE (IF_DESKTOP(option_mask32 & 2) IF_NOT_DESKTOP(0))
  46#define OPT_CHANGED (IF_DESKTOP(option_mask32 & 4) IF_NOT_DESKTOP(0))
  47#define OPT_QUIET   (IF_DESKTOP(option_mask32 & 8) IF_NOT_DESKTOP(0))
  48#define OPT_STR     "R" IF_DESKTOP("vcf")
  49
  50/* coreutils:
  51 * chmod never changes the permissions of symbolic links; the chmod
  52 * system call cannot change their permissions. This is not a problem
  53 * since the permissions of symbolic links are never used.
  54 * However, for each symbolic link listed on the command line, chmod changes
  55 * the permissions of the pointed-to file. In contrast, chmod ignores
  56 * symbolic links encountered during recursive directory traversals.
  57 */
  58
  59static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void* param, int depth)
  60{
  61        mode_t newmode;
  62
  63        /* match coreutils behavior */
  64        if (depth == 0) {
  65                /* statbuf holds lstat result, but we need stat (follow link) */
  66                if (stat(fileName, statbuf))
  67                        goto err;
  68        } else { /* depth > 0: skip links */
  69                if (S_ISLNK(statbuf->st_mode))
  70                        return TRUE;
  71        }
  72        newmode = statbuf->st_mode;
  73
  74        if (!bb_parse_mode((char *)param, &newmode))
  75                bb_error_msg_and_die("invalid mode '%s'", (char *)param);
  76
  77        if (chmod(fileName, newmode) == 0) {
  78                if (OPT_VERBOSE
  79                 || (OPT_CHANGED && statbuf->st_mode != newmode)
  80                ) {
  81                        printf("mode of '%s' changed to %04o (%s)\n", fileName,
  82                                newmode & 07777, bb_mode_string(newmode)+1);
  83                }
  84                return TRUE;
  85        }
  86 err:
  87        if (!OPT_QUIET)
  88                bb_simple_perror_msg(fileName);
  89        return FALSE;
  90}
  91
  92int chmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  93int chmod_main(int argc UNUSED_PARAM, char **argv)
  94{
  95        int retval = EXIT_SUCCESS;
  96        char *arg, **argp;
  97        char *smode;
  98
  99        /* Convert first encountered -r into ar, -w into aw etc
 100         * so that getopt would not eat it */
 101        argp = argv;
 102        while ((arg = *++argp)) {
 103                /* Mode spec must be the first arg (sans -R etc) */
 104                /* (protect against mishandling e.g. "chmod 644 -r") */
 105                if (arg[0] != '-') {
 106                        arg = NULL;
 107                        break;
 108                }
 109                /* An option. Not a -- or valid option? */
 110                if (arg[1] && !strchr("-"OPT_STR, arg[1])) {
 111                        arg[0] = 'a';
 112                        break;
 113                }
 114        }
 115
 116        /* Parse options */
 117        opt_complementary = "-2";
 118        getopt32(argv, ("-"OPT_STR) + 1); /* Reuse string */
 119        argv += optind;
 120
 121        /* Restore option-like mode if needed */
 122        if (arg) arg[0] = '-';
 123
 124        /* Ok, ready to do the deed now */
 125        smode = *argv++;
 126        do {
 127                if (!recursive_action(*argv,
 128                        OPT_RECURSE,    // recurse
 129                        fileAction,     // file action
 130                        fileAction,     // dir action
 131                        smode,          // user data
 132                        0)              // depth
 133                ) {
 134                        retval = EXIT_FAILURE;
 135                }
 136        } while (*++argv);
 137
 138        return retval;
 139}
 140
 141/*
 142Security: chmod is too important and too subtle.
 143This is a test script (busybox chmod versus coreutils).
 144Run it in empty directory.
 145
 146#!/bin/sh
 147t1="/tmp/busybox chmod"
 148t2="/usr/bin/chmod"
 149create() {
 150    rm -rf $1; mkdir $1
 151    (
 152    cd $1 || exit 1
 153    mkdir dir
 154    >up
 155    >file
 156    >dir/file
 157    ln -s dir linkdir
 158    ln -s file linkfile
 159    ln -s ../up dir/up
 160    )
 161}
 162tst() {
 163    (cd test1; $t1 $1)
 164    (cd test2; $t2 $1)
 165    (cd test1; ls -lR) >out1
 166    (cd test2; ls -lR) >out2
 167    echo "chmod $1" >out.diff
 168    if ! diff -u out1 out2 >>out.diff; then exit 1; fi
 169    rm out.diff
 170}
 171echo "If script produced 'out.diff' file, then at least one testcase failed"
 172create test1; create test2
 173tst "a+w file"
 174tst "a-w dir"
 175tst "a+w linkfile"
 176tst "a-w linkdir"
 177tst "-R a+w file"
 178tst "-R a-w dir"
 179tst "-R a+w linkfile"
 180tst "-R a-w linkdir"
 181tst "a-r,a+x linkfile"
 182*/
 183