4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
30 * Portions of this source code were derived from Berkeley 4.3 BSD
31 * under license from the Regents of the University of California.
34 #pragma ident "%Z%%M% %I% %E% SMI"
37 * chown [-fhR] uid[:gid] file ...
38 * chown -R [-f] [-H|-L|-P] uid[:gid] file ...
39 * chown -s [-fhR] ownersid[:groupsid] file ...
40 * chown -s -R [-f] [-H|-L|-P] ownersid[:groupsid] file ...
46 #include <sys/types.h>
56 #include <libcmdutils.h>
59 static struct passwd
*pwd
;
60 static struct group
*grp
;
61 static struct stat stbuf
;
62 static uid_t uid
= (uid_t
)-1;
63 static gid_t gid
= (gid_t
)-1;
64 static int status
= 0; /* total number of errors received */
72 static avl_tree_t
*tree
;
74 static int Perror(char *);
75 static int isnumber(char *);
76 static void chownr(char *, uid_t
, gid_t
);
81 * Check to see if we are to follow symlinks specified on the command line.
82 * This assumes we've already checked to make sure neither -h or -P was
83 * specified, so we are just looking to see if -R -H, or -R -L was specified,
84 * or, since -R has the same behavior as -R -L, if -R was specified by itself.
85 * Therefore, all we really need to check for is if -R was specified.
87 #define FOLLOW_CL_LINKS (rflag)
90 * Check to see if we are to follow symlinks specified on the command line.
91 * This assumes we've already checked to make sure neither -h or -P was
92 * specified, so we are just looking to see if -R -H, or -R -L was specified.
93 * Note: -R by itself will change the ownership of a directory referenced by a
94 * symlink however it will now follow the symlink to any other part of the
97 #define FOLLOW_CL_LINKS (rflag && (Hflag || Lflag))
102 * Follow symlinks when traversing directories. Since -R behaves the
103 * same as -R -L, we always want to follow symlinks to other parts
104 * of the file hierarchy unless -H was specified.
106 #define FOLLOW_D_LINKS (!Hflag)
109 * Follow symlinks when traversing directories. Only follow symlinks
110 * to other parts of the file hierarchy if -L was specified.
112 #define FOLLOW_D_LINKS (Lflag)
115 #define CHOWN(f, u, g) if (chown(f, u, g) < 0) { \
116 status += Perror(f); \
118 #define LCHOWN(f, u, g) if (lchown(f, u, g) < 0) { \
119 status += Perror(f); \
124 main(int argc
, char *argv
[])
128 char *grpp
; /* pointer to group name arg */
132 (void) setlocale(LC_ALL
, "");
133 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
134 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
136 (void) textdomain(TEXT_DOMAIN
);
138 while ((ch
= getopt(argc
, argv
, "hRfHLPs")) != EOF
) {
154 * If more than one of -H, -L, and -P
155 * are specified, only the last option
156 * specified determines the behavior of
183 * Check for sufficient arguments
188 argv
= &argv
[optind
];
190 if (errflg
|| (argc
< 2) ||
191 ((Hflag
|| Lflag
|| Pflag
) && !rflag
) ||
192 ((Hflag
|| Lflag
|| Pflag
) && hflag
)) {
198 * Check for owner[:group]
200 if ((grpp
= strchr(argv
[0], ':')) != NULL
) {
204 if (sid_to_id(grpp
, B_FALSE
, &gid
)) {
205 (void) fprintf(stderr
, gettext(
206 "chown: invalid owning group sid %s\n"),
210 } else if ((grp
= getgrnam(grpp
)) != NULL
) {
213 if (isnumber(grpp
)) {
215 gid
= (gid_t
)strtoul(grpp
, NULL
, 10);
217 if (errno
== ERANGE
) {
218 (void) fprintf(stderr
, gettext(
219 "chown: group id too large\n"));
222 (void) fprintf(stderr
, gettext(
223 "chown: invalid group id\n"));
228 (void) fprintf(stderr
, gettext(
229 "chown: unknown group id %s\n"), grpp
);
236 if (sid_to_id(argv
[0], B_TRUE
, &uid
)) {
237 (void) fprintf(stderr
, gettext(
238 "chown: invalid owner sid %s\n"), argv
[0]);
241 } else if ((pwd
= getpwnam(argv
[0])) != NULL
) {
244 if (isnumber(argv
[0])) {
246 uid
= (uid_t
)strtoul(argv
[0], NULL
, 10);
248 if (errno
== ERANGE
) {
249 (void) fprintf(stderr
, gettext(
250 "chown: user id too large\n"));
253 (void) fprintf(stderr
, gettext(
254 "chown: invalid user id\n"));
259 (void) fprintf(stderr
, gettext(
260 "chown: unknown user id %s\n"), argv
[0]);
265 for (c
= 1; c
< argc
; c
++) {
267 if (lstat(argv
[c
], &stbuf
) < 0) {
268 status
+= Perror(argv
[c
]);
271 if (rflag
&& ((stbuf
.st_mode
& S_IFMT
) == S_IFLNK
)) {
272 if (hflag
|| Pflag
) {
274 * Change the ownership of the symlink
275 * specified on the command line.
276 * Don't follow the symbolic link to
277 * any other part of the file hierarchy.
279 LCHOWN(argv
[c
], uid
, gid
);
282 if (stat(argv
[c
], &stbuf2
) < 0) {
283 status
+= Perror(argv
[c
]);
287 * We know that we are to change the
288 * ownership of the file referenced by the
289 * symlink specified on the command line.
290 * Now check to see if we are to follow
291 * the symlink to any other part of the
294 if (FOLLOW_CL_LINKS
) {
295 if ((stbuf2
.st_mode
& S_IFMT
)
298 * We are following symlinks so
299 * traverse into the directory.
300 * Add this node to the search
301 * tree so we don't get into an
306 stbuf2
.st_ino
) == 1) {
313 * as this is the first
314 * node to be added to
322 * Change the user ID of the
323 * file referenced by the
326 CHOWN(argv
[c
], uid
, gid
);
330 * Change the user ID of the file
331 * referenced by the symbolic link.
333 CHOWN(argv
[c
], uid
, gid
);
336 } else if (rflag
&& ((stbuf
.st_mode
& S_IFMT
) == S_IFDIR
)) {
338 * Add this node to the search tree so we don't
339 * get into a endless loop.
341 if (add_tnode(&tree
, stbuf
.st_dev
,
342 stbuf
.st_ino
) == 1) {
343 chownr(argv
[c
], uid
, gid
);
346 * An error occurred while trying
347 * to add the node to the tree.
348 * Continue on with next file
349 * specified. Note: rc shouldn't
350 * be 0 as this was the first node
351 * being added to the search tree.
353 status
+= Perror(argv
[c
]);
355 } else if (hflag
|| Pflag
) {
356 LCHOWN(argv
[c
], uid
, gid
);
358 CHOWN(argv
[c
], uid
, gid
);
365 * chownr() - recursive chown()
367 * Recursively chowns the input directory then its contents. rflag must
368 * have been set if chownr() is called. The input directory should not
369 * be a sym link (this is handled in the calling routine). In
370 * addition, the calling routine should have already added the input
371 * directory to the search tree so we do not get into endless loops.
372 * Note: chownr() doesn't need a return value as errors are reported
373 * through the global "status" variable.
376 chownr(char *dir
, uid_t uid
, gid_t gid
)
383 if (getcwd(savedir
, 1024) == NULL
) {
384 (void) Perror("getcwd");
389 * Attempt to chown the directory, however don't return if we
390 * can't as we still may be able to chown the contents of the
391 * directory. Note: the calling routine resets the SUID bits
392 * on this directory so we don't have to perform an extra 'stat'.
394 CHOWN(dir
, uid
, gid
);
396 if (chdir(dir
) < 0) {
397 status
+= Perror(dir
);
400 if ((dirp
= opendir(".")) == NULL
) {
401 status
+= Perror(dir
);
404 for (dp
= readdir(dirp
); dp
!= NULL
; dp
= readdir(dirp
)) {
405 if (strcmp(dp
->d_name
, ".") == 0 || /* skip . and .. */
406 strcmp(dp
->d_name
, "..") == 0) {
409 if (lstat(dp
->d_name
, &st
) < 0) {
410 status
+= Perror(dp
->d_name
);
413 if ((st
.st_mode
& S_IFMT
) == S_IFLNK
) {
414 if (hflag
|| Pflag
) {
416 * Change the ownership of the symbolic link
417 * encountered while traversing the
418 * directory. Don't follow the symbolic
419 * link to any other part of the file
422 LCHOWN(dp
->d_name
, uid
, gid
);
424 if (stat(dp
->d_name
, &st2
) < 0) {
425 status
+= Perror(dp
->d_name
);
429 * We know that we are to change the
430 * ownership of the file referenced by the
431 * symlink encountered while traversing
432 * the directory. Now check to see if we
433 * are to follow the symlink to any other
434 * part of the file hierarchy.
436 if (FOLLOW_D_LINKS
) {
437 if ((st2
.st_mode
& S_IFMT
) == S_IFDIR
) {
439 * We are following symlinks so
440 * traverse into the directory.
441 * Add this node to the search
442 * tree so we don't get into an
446 if ((rc
= add_tnode(&tree
,
451 } else if (rc
== 0) {
452 /* already visited */
457 * while trying to add
458 * the node to the tree.
466 * Change the user id of the
467 * file referenced by the
470 CHOWN(dp
->d_name
, uid
, gid
);
474 * Change the user id of the file
475 * referenced by the symbolic link.
477 CHOWN(dp
->d_name
, uid
, gid
);
480 } else if ((st
.st_mode
& S_IFMT
) == S_IFDIR
) {
482 * Add this node to the search tree so we don't
483 * get into a endless loop.
486 if ((rc
= add_tnode(&tree
, st
.st_dev
,
488 chownr(dp
->d_name
, uid
, gid
);
489 } else if (rc
== 0) {
490 /* already visited */
494 * An error occurred while trying
495 * to add the node to the search tree.
497 status
+= Perror(dp
->d_name
);
501 CHOWN(dp
->d_name
, uid
, gid
);
505 (void) closedir(dirp
);
506 if (chdir(savedir
) < 0) {
507 (void) fprintf(stderr
, gettext(
508 "chown: can't change back to %s\n"), savedir
);
518 while ((c
= *s
++) != '\0')
528 (void) fprintf(stderr
, "chown: ");
537 (void) fprintf(stderr
, gettext(
539 "\tchown [-fhR] owner[:group] file...\n"
540 "\tchown -R [-f] [-H|-L|-P] owner[:group] file...\n"
541 "\tchown -s [-fhR] ownersid[:groupsid] file...\n"
542 "\tchown -s -R [-f] [-H|-L|-P] ownersid[:groupsid] file...\n"));