1/* See LICENSE file for copyright and license details. */
2
3#include <errno.h>
4#include <fcntl.h>
5#include <grp.h>
6#include <limits.h>
7#include <pwd.h>
8#include <stdlib.h>
9#include <string.h>
10#include <unistd.h>
11
12#include "fs.h"
13#include "util.h"
14
15static int hflag = 0;
16static uid_t uid = -1;
17static gid_t gid = -1;
18static int ret = 0;
19
20static void
21chownpwgr(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r)
22{
23 int flags = 0;
24
25 (void)data;
26
27 if ((r->maxdepth == 0 && r->follow == 'P') || (r->follow == 'H' && r->depth)
28 || (hflag && !(r->depth)))
29 flags |= AT_SYMLINK_NOFOLLOW;
30
31 if (fchownat(dirfd, name, uid, gid, flags) < 0) {
32 weprintf("chown %s:", r->path);
33 ret = 1;
34 } else if (S_ISDIR(st->st_mode)) {
35 recurse(dirfd, name, NULL, r);
36 }
37}
38
39static void
40usage(void)
41{
42 eprintf(
43 "usage: %s [-h] [-R [-H | -L | -P]] owner[:[group]] file ...\n"
44 " %s [-h] [-R [-H | -L | -P]] :group file ...\n",
45 argv0,
46 argv0
47 );
48}
49
50// ?man chown: change ownership
51// ?man arguments: owner[:[group]] file ...
52// ?man change the user and group ownership of files and directories
53int
54main(int argc, char *argv[])
55{
56 struct group *gr;
57 struct passwd *pw;
58 struct recursor r = {.fn = chownpwgr, .maxdepth = 1, .follow = 'P'};
59 char *owner, *group;
60
61 ARGBEGIN
62 {
63 // ?man -h: affect symbolic links instead of referenced files
64 case 'h':
65 hflag = 1;
66 break;
67 // ?man -r: operate recursively
68 case 'r':
69 // ?man -R: change files and directories recursively
70 case 'R':
71 r.maxdepth = 0;
72 break;
73 // ?man -H: specify option flag
74 case 'H':
75 // ?man -L: specify option flag
76 case 'L':
77 // ?man -P: specify option flag
78 case 'P':
79 r.follow = ARGC();
80 break;
81 default:
82 usage();
83 }
84 ARGEND
85
86 if (argc < 2)
87 usage();
88
89 owner = argv[0];
90 if ((group = strchr(owner, ':')))
91 *group++ = '\0';
92
93 if (owner && *owner) {
94 errno = 0;
95 pw = getpwnam(owner);
96 if (pw) {
97 uid = pw->pw_uid;
98 } else {
99 if (errno)
100 eprintf("getpwnam %s:", owner);
101 uid = estrtonum(owner, 0, UINT_MAX);
102 }
103 }
104 if (group && *group) {
105 errno = 0;
106 gr = getgrnam(group);
107 if (gr) {
108 gid = gr->gr_gid;
109 } else {
110 if (errno)
111 eprintf("getgrnam %s:", group);
112 gid = estrtonum(group, 0, UINT_MAX);
113 }
114 }
115 if (uid == (uid_t)-1 && gid == (gid_t)-1)
116 usage();
117
118 for (argc--, argv++; *argv; argc--, argv++)
119 recurse(AT_FDCWD, *argv, NULL, &r);
120
121 return ret || recurse_status;
122}