summaryrefslogtreecommitdiff
path: root/coreutils/chown.c (plain)
blob: 50b06d73a1ce69fa6d37ff767d2edc1dd48d75f4
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 source tree.
8 */
9//config:config CHOWN
10//config: bool "chown"
11//config: default y
12//config: help
13//config: chown is used to change the user and/or group ownership
14//config: of files.
15//config:
16//config:config FEATURE_CHOWN_LONG_OPTIONS
17//config: bool "Enable long options"
18//config: default y
19//config: depends on CHOWN && LONG_OPTS
20//config: help
21//config: Enable use of long options
22
23//applet:IF_CHOWN(APPLET_NOEXEC(chown, chown, BB_DIR_BIN, BB_SUID_DROP, chown))
24
25//kbuild:lib-$(CONFIG_CHOWN) += chown.o
26
27/* BB_AUDIT SUSv3 defects - none? */
28/* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */
29
30//usage:#define chown_trivial_usage
31//usage: "[-Rh"IF_DESKTOP("LHPcvf")"]... USER[:[GRP]] FILE..."
32//usage:#define chown_full_usage "\n\n"
33//usage: "Change the owner and/or group of each FILE to USER and/or GRP\n"
34//usage: "\n -R Recurse"
35//usage: "\n -h Affect symlinks instead of symlink targets"
36//usage: IF_DESKTOP(
37//usage: "\n -L Traverse all symlinks to directories"
38//usage: "\n -H Traverse symlinks on command line only"
39//usage: "\n -P Don't traverse symlinks (default)"
40//usage: "\n -c List changed files"
41//usage: "\n -v List all files"
42//usage: "\n -f Hide errors"
43//usage: )
44//usage:
45//usage:#define chown_example_usage
46//usage: "$ ls -l /tmp/foo\n"
47//usage: "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n"
48//usage: "$ chown root /tmp/foo\n"
49//usage: "$ ls -l /tmp/foo\n"
50//usage: "-r--r--r-- 1 root andersen 0 Apr 12 18:25 /tmp/foo\n"
51//usage: "$ chown root.root /tmp/foo\n"
52//usage: "ls -l /tmp/foo\n"
53//usage: "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n"
54
55#include "libbb.h"
56
57/* This is a NOEXEC applet. Be very careful! */
58
59
60#define OPT_STR ("Rh" IF_DESKTOP("vcfLHP"))
61#define BIT_RECURSE 1
62#define OPT_RECURSE (opt & 1)
63#define OPT_NODEREF (opt & 2)
64#define OPT_VERBOSE (IF_DESKTOP(opt & 0x04) IF_NOT_DESKTOP(0))
65#define OPT_CHANGED (IF_DESKTOP(opt & 0x08) IF_NOT_DESKTOP(0))
66#define OPT_QUIET (IF_DESKTOP(opt & 0x10) IF_NOT_DESKTOP(0))
67/* POSIX options
68 * -L traverse every symbolic link to a directory encountered
69 * -H if a command line argument is a symbolic link to a directory, traverse it
70 * -P do not traverse any symbolic links (default)
71 * We do not conform to the following:
72 * "Specifying more than one of -H, -L, and -P is not an error.
73 * The last option specified shall determine the behavior of the utility." */
74/* -L */
75#define BIT_TRAVERSE 0x20
76#define OPT_TRAVERSE (IF_DESKTOP(opt & BIT_TRAVERSE) IF_NOT_DESKTOP(0))
77/* -H or -L */
78#define BIT_TRAVERSE_TOP (0x20|0x40)
79#define OPT_TRAVERSE_TOP (IF_DESKTOP(opt & BIT_TRAVERSE_TOP) IF_NOT_DESKTOP(0))
80
81#if ENABLE_FEATURE_CHOWN_LONG_OPTIONS
82static const char chown_longopts[] ALIGN1 =
83 "recursive\0" No_argument "R"
84 "dereference\0" No_argument "\xff"
85 "no-dereference\0" No_argument "h"
86# if ENABLE_DESKTOP
87 "changes\0" No_argument "c"
88 "silent\0" No_argument "f"
89 "quiet\0" No_argument "f"
90 "verbose\0" No_argument "v"
91# endif
92 ;
93#endif
94
95typedef int (*chown_fptr)(const char *, uid_t, gid_t);
96
97struct param_t {
98 struct bb_uidgid_t ugid;
99 chown_fptr chown_func;
100};
101
102static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf,
103 void *vparam, int depth UNUSED_PARAM)
104{
105#define param (*(struct param_t*)vparam)
106#define opt option_mask32
107 uid_t u = (param.ugid.uid == (uid_t)-1L) ? statbuf->st_uid : param.ugid.uid;
108 gid_t g = (param.ugid.gid == (gid_t)-1L) ? statbuf->st_gid : param.ugid.gid;
109
110 if (param.chown_func(fileName, u, g) == 0) {
111 if (OPT_VERBOSE
112 || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g))
113 ) {
114 printf("changed ownership of '%s' to %u:%u\n",
115 fileName, (unsigned)u, (unsigned)g);
116 }
117 return TRUE;
118 }
119 if (!OPT_QUIET)
120 bb_simple_perror_msg(fileName);
121 return FALSE;
122#undef opt
123#undef param
124}
125
126int chown_main(int argc UNUSED_PARAM, char **argv)
127{
128 int retval = EXIT_SUCCESS;
129 int opt, flags;
130 struct param_t param;
131
132#if ENABLE_FEATURE_CHOWN_LONG_OPTIONS
133 applet_long_options = chown_longopts;
134#endif
135 opt_complementary = "-2";
136 opt = getopt32(argv, OPT_STR);
137 argv += optind;
138
139 /* This matches coreutils behavior (almost - see below) */
140 param.chown_func = chown;
141 if (OPT_NODEREF
142 /* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */
143 IF_DESKTOP( || (opt & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE)
144 ) {
145 param.chown_func = lchown;
146 }
147
148 flags = ACTION_DEPTHFIRST; /* match coreutils order */
149 if (OPT_RECURSE)
150 flags |= ACTION_RECURSE;
151 if (OPT_TRAVERSE_TOP)
152 flags |= ACTION_FOLLOWLINKS_L0; /* -H/-L: follow links on depth 0 */
153 if (OPT_TRAVERSE)
154 flags |= ACTION_FOLLOWLINKS; /* follow links if -L */
155
156 parse_chown_usergroup_or_die(&param.ugid, argv[0]);
157
158 /* Ok, ready to do the deed now */
159 while (*++argv) {
160 if (!recursive_action(*argv,
161 flags, /* flags */
162 fileAction, /* file action */
163 fileAction, /* dir action */
164 &param, /* user data */
165 0) /* depth */
166 ) {
167 retval = EXIT_FAILURE;
168 }
169 }
170
171 return retval;
172}
173
174/*
175Testcase. Run in empty directory.
176
177#!/bin/sh
178t1="/tmp/busybox chown"
179t2="/usr/bin/chown"
180create() {
181 rm -rf $1; mkdir $1
182 (
183 cd $1 || exit 1
184 mkdir dir dir2
185 >up
186 >file
187 >dir/file
188 >dir2/file
189 ln -s dir linkdir
190 ln -s file linkfile
191 ln -s ../up dir/linkup
192 ln -s ../dir2 dir/linkupdir2
193 )
194 chown -R 0:0 $1
195}
196tst() {
197 create test1
198 create test2
199 echo "[$1]" >>test1.out
200 echo "[$1]" >>test2.out
201 (cd test1; $t1 $1) >>test1.out 2>&1
202 (cd test2; $t2 $1) >>test2.out 2>&1
203 (cd test1; ls -lnR) >out1
204 (cd test2; ls -lnR) >out2
205 echo "chown $1" >out.diff
206 if ! diff -u out1 out2 >>out.diff; then exit 1; fi
207 rm out.diff
208}
209tst_for_each() {
210 tst "$1 1:1 file"
211 tst "$1 1:1 dir"
212 tst "$1 1:1 linkdir"
213 tst "$1 1:1 linkfile"
214}
215echo "If script produced 'out.diff' file, then at least one testcase failed"
216>test1.out
217>test2.out
218# These match coreutils 6.8:
219tst_for_each "-v"
220tst_for_each "-vR"
221tst_for_each "-vRP"
222tst_for_each "-vRL"
223tst_for_each "-vRH"
224tst_for_each "-vh"
225tst_for_each "-vhR"
226tst_for_each "-vhRP"
227tst_for_each "-vhRL"
228tst_for_each "-vhRH"
229# Fix `name' in coreutils output
230sed 's/`/'"'"'/g' -i test2.out
231# Compare us with coreutils output
232diff -u test1.out test2.out
233
234*/
235