-
Notifications
You must be signed in to change notification settings - Fork 53
/
Copy pathchown
executable file
·212 lines (158 loc) · 5.18 KB
/
chown
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#!/usr/bin/perl
=begin metadata
Name: chown
Description: change ownership of files
Author: Abigail, [email protected]
License: perl
=end metadata
=cut
use strict;
use File::Basename qw(basename);
use constant EX_SUCCESS => 0;
use constant EX_FAILURE => 1;
my $Program = basename($0);
my ($VERSION) = '1.4';
my $rc = EX_SUCCESS;
sub usage {
warn "$Program (Perl bin utils) $VERSION\n";
warn "usage: $Program [-R [-H | -L | -P]] user[:group] file [files ...]\n";
exit EX_FAILURE;
}
# Get the options.
# We can't use Getopts, as the order is important.
my %options;
while (@ARGV && $ARGV [0] =~ /^-/) {
my $opt = reverse shift;
chop $opt;
last if ($opt eq '-');
usage() unless $opt =~ /^[RHLP]+$/;
local $_;
while (length ($_ = chop $opt)) {
/R/ && do {$options {R} = 1; next};
usage() unless $options{R};
/H/ && do {$options {L} = $options {P} = 0; $options {H} = 1; next};
/L/ && do {$options {H} = $options {P} = 0; $options {L} = 1; next};
/P/ && do {$options {H} = $options {L} = 0; $options {P} = 1; next};
}
}
my $mode = shift;
usage() unless @ARGV;
my ($owner, $group) = split /:/ => $mode, 2;
my $uid = getpwnam $owner;
unless (defined $uid) {
$uid = $owner if $owner =~ m/\A[0-9]+\z/;
}
unless (defined $uid) {
warn "$Program: invalid user: '$owner'\n";
exit EX_FAILURE;
}
my $gid;
if (defined $group) {
$gid = getgrnam $group;
unless (defined $gid) {
$gid = $group if $group =~ m/\A[0-9]+\z/;
}
unless (defined $gid) {
warn "$Program: invalid group: '$group'\n";
exit EX_FAILURE;
}
}
my %ARGV;
%ARGV = map {$_ => 1} @ARGV if $options {H};
if (exists $options {R}) {
# Recursion.
require File::Find;
File::Find::find (\&modify_file, @ARGV);
}
else {
foreach my $file (@ARGV) {
modify_file($file);
}
}
exit $rc;
# File::Find is weird. If called with a directory, it will call
# the sub with "." as file name, while having chdir()ed to the
# directory. But it doesn't do that in recursion, just the top
# level ones. And it ain't true that $File::Find::name eq
# "$File::Find::dir/$_" in all cases.
# But it shouldn't matter in this case.
sub modify_file {
my $file = @_ ? shift : $_;
# Now, if this is a symbolic link, it points somewhere,
# *and* we are following symbolic links, we recurse.
# This may never end as symlinks can form loops.
if (-l $file && -e $file &&
($options {L} || $options {H} && $ARGV {$file})) {
# We don't want to recurse symlinks that just happen to
# have the same name as one of the arguments, hence the local.
# Remember that $file is relative to the current directory.
local $ARGV {readlink $file} = 0;
File::Find::find (\&modify_file, readlink $file);
return;
}
unless (defined $group) {
my @st = stat $file;
unless (@st) {
warn "$Program: failed to stat '$file': $!\n";
$rc = EX_FAILURE;
return;
}
$gid = $st[5];
}
unless (chown $uid, $gid, $file) {
warn "$Program: '$file': $!\n";
$rc = EX_FAILURE;
}
}
__END__
=pod
=head1 NAME
chown - change ownership of files
=head1 SYNOPSIS
chown [-R [-H | -L | -P]] user[:group] file [files ...]
=head1 DESCRIPTION
I<chown> sets the ownership of files. The first argument after the
options is either a I<user>, or a I<user>-I<group> pair, separated
by a colon. If the I<group> is given, group membership of the file
is changed as well.
=head2 OPTIONS
I<chown> accepts the options described below. The options I<-L>,
I<-H> and I<-P> are mutually exclusive, and only the last given
option will be honoured. All of I<-L>, I<-H> and I<-P> require the
I<-R> option to be set first.
=over 4
=item -R
Recurse into directories. Any directories are recursively traversed,
and all files and directories will change owner.
=item -L
Follow symbolic links. By default, I<chown> will not follow symbolic
links. This is a potential dangerous option, as I<chown> will not
check for cycles. Be careful. This option requires the I<-R> option to be set.
=item -H
Follow symbolic links of command line files/directories only. This option
requires the I<-R> option to be set.
=item -P
Do not follow symbolic links at all. This option requires the I<-R> option
to be set.
=back
=head1 ENVIRONMENT
The working of I<chown> is not influenced by any environment variables.
=head1 BUGS
I<chown> can loop forever when symbolic links create cycles.
I<chown> uses C<File::Find> to recurse.
=head1 REVISION HISTORY
$Log: chown,v $
Revision 1.2 2004/08/05 14:17:43 cwest
cleanup, new version number on website
Revision 1.1 2004/07/23 20:10:01 cwest
initial import
Revision 1.1 1999/02/28 19:59:17 abigail
Initial revision
=head1 AUTHOR
The Perl implementation of I<chown> was written by Abigail, I<[email protected]>.
=head1 COPYRIGHT and LICENSE
This program is copyright by Abigail 1999.
This program is free and open software. You may use, copy, modify, distribute
and sell this program (and any modified variants) in any way you wish,
provided you do not restrict others to do the same.
=cut