busybox/coreutils/chown.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini chown implementation for busybox
   4 *
   5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
   8 */
   9
  10/* BB_AUDIT SUSv3 defects - none? */
  11/* BB_AUDIT GNU defects - unsupported long options. */
  12/* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */
  13
  14#include "libbb.h"
  15
  16/* This is a NOEXEC applet. Be very careful! */
  17
  18
  19#define OPT_STR     ("Rh" IF_DESKTOP("vcfLHP"))
  20#define BIT_RECURSE 1
  21#define OPT_RECURSE (opt & 1)
  22#define OPT_NODEREF (opt & 2)
  23#define OPT_VERBOSE (IF_DESKTOP(opt & 0x04) IF_NOT_DESKTOP(0))
  24#define OPT_CHANGED (IF_DESKTOP(opt & 0x08) IF_NOT_DESKTOP(0))
  25#define OPT_QUIET   (IF_DESKTOP(opt & 0x10) IF_NOT_DESKTOP(0))
  26/* POSIX options
  27 * -L traverse every symbolic link to a directory encountered
  28 * -H if a command line argument is a symbolic link to a directory, traverse it
  29 * -P do not traverse any symbolic links (default)
  30 * We do not conform to the following:
  31 * "Specifying more than one of -H, -L, and -P is not an error.
  32 * The last option specified shall determine the behavior of the utility." */
  33/* -L */
  34#define BIT_TRAVERSE 0x20
  35#define OPT_TRAVERSE (IF_DESKTOP(opt & BIT_TRAVERSE) IF_NOT_DESKTOP(0))
  36/* -H or -L */
  37#define BIT_TRAVERSE_TOP (0x20|0x40)
  38#define OPT_TRAVERSE_TOP (IF_DESKTOP(opt & BIT_TRAVERSE_TOP) IF_NOT_DESKTOP(0))
  39
  40typedef int (*chown_fptr)(const char *, uid_t, gid_t);
  41
  42struct param_t {
  43        struct bb_uidgid_t ugid;
  44        chown_fptr chown_func;
  45};
  46
  47static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf,
  48                void *vparam, int depth UNUSED_PARAM)
  49{
  50#define param  (*(struct param_t*)vparam)
  51#define opt option_mask32
  52        uid_t u = (param.ugid.uid == (uid_t)-1) ? statbuf->st_uid : param.ugid.uid;
  53        gid_t g = (param.ugid.gid == (gid_t)-1) ? statbuf->st_gid : param.ugid.gid;
  54
  55        if (param.chown_func(fileName, u, g) == 0) {
  56                if (OPT_VERBOSE
  57                 || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g))
  58                ) {
  59                        printf("changed ownership of '%s' to %u:%u\n",
  60                                        fileName, (unsigned)u, (unsigned)g);
  61                }
  62                return TRUE;
  63        }
  64        if (!OPT_QUIET)
  65                bb_simple_perror_msg(fileName); /* A filename can have % in it... */
  66        return FALSE;
  67#undef opt
  68#undef param
  69}
  70
  71int chown_main(int argc UNUSED_PARAM, char **argv)
  72{
  73        int retval = EXIT_SUCCESS;
  74        int opt, flags;
  75        struct param_t param;
  76
  77        param.ugid.uid = -1;
  78        param.ugid.gid = -1;
  79        param.chown_func = chown;
  80
  81        opt_complementary = "-2";
  82        opt = getopt32(argv, OPT_STR);
  83        argv += optind;
  84
  85        /* This matches coreutils behavior (almost - see below) */
  86        if (OPT_NODEREF
  87            /* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */
  88            IF_DESKTOP( || (opt & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE)
  89        ) {
  90                param.chown_func = lchown;
  91        }
  92
  93        flags = ACTION_DEPTHFIRST; /* match coreutils order */
  94        if (OPT_RECURSE)
  95                flags |= ACTION_RECURSE;
  96        if (OPT_TRAVERSE_TOP)
  97                flags |= ACTION_FOLLOWLINKS_L0; /* -H/-L: follow links on depth 0 */
  98        if (OPT_TRAVERSE)
  99                flags |= ACTION_FOLLOWLINKS; /* follow links if -L */
 100
 101        parse_chown_usergroup_or_die(&param.ugid, argv[0]);
 102
 103        /* Ok, ready to do the deed now */
 104        argv++;
 105        do {
 106                if (!recursive_action(*argv,
 107                                flags,          /* flags */
 108                                fileAction,     /* file action */
 109                                fileAction,     /* dir action */
 110                                &param,         /* user data */
 111                                0)              /* depth */
 112                ) {
 113                        retval = EXIT_FAILURE;
 114                }
 115        } while (*++argv);
 116
 117        return retval;
 118}
 119
 120/*
 121Testcase. Run in empty directory.
 122
 123#!/bin/sh
 124t1="/tmp/busybox chown"
 125t2="/usr/bin/chown"
 126create() {
 127    rm -rf $1; mkdir $1
 128    (
 129    cd $1 || exit 1
 130    mkdir dir dir2
 131    >up
 132    >file
 133    >dir/file
 134    >dir2/file
 135    ln -s dir linkdir
 136    ln -s file linkfile
 137    ln -s ../up dir/linkup
 138    ln -s ../dir2 dir/linkupdir2
 139    )
 140    chown -R 0:0 $1
 141}
 142tst() {
 143    create test1
 144    create test2
 145    echo "[$1]" >>test1.out
 146    echo "[$1]" >>test2.out
 147    (cd test1; $t1 $1) >>test1.out 2>&1
 148    (cd test2; $t2 $1) >>test2.out 2>&1
 149    (cd test1; ls -lnR) >out1
 150    (cd test2; ls -lnR) >out2
 151    echo "chown $1" >out.diff
 152    if ! diff -u out1 out2 >>out.diff; then exit 1; fi
 153    rm out.diff
 154}
 155tst_for_each() {
 156    tst "$1 1:1 file"
 157    tst "$1 1:1 dir"
 158    tst "$1 1:1 linkdir"
 159    tst "$1 1:1 linkfile"
 160}
 161echo "If script produced 'out.diff' file, then at least one testcase failed"
 162>test1.out
 163>test2.out
 164# These match coreutils 6.8:
 165tst_for_each "-v"
 166tst_for_each "-vR"
 167tst_for_each "-vRP"
 168tst_for_each "-vRL"
 169tst_for_each "-vRH"
 170tst_for_each "-vh"
 171tst_for_each "-vhR"
 172tst_for_each "-vhRP"
 173tst_for_each "-vhRL"
 174tst_for_each "-vhRH"
 175# Fix `name' in coreutils output
 176sed 's/`/'"'"'/g' -i test2.out
 177# Compare us with coreutils output
 178diff -u test1.out test2.out
 179
 180*/
 181