Jim Davis jdavis@cs.arizona.edu wrote: [...]
- is there some slick way to reassign UIDs en masse on a filer? We could
run 'chown -R ...' on the admin host, but is there a better way?
Be _very_ careful about using "chown -R". In particular, worry about whether it follows symlinks ("chown -h -R" would be safer in Solaris, for example). Also worry about users hard-linking each others' files.
Ben Rockwood BRockwood@homestead-inc.com responds: [...]
As for re-assigning, sounds like a job for PERL or some expertly crafted "find" commands. You could create an index in a 2-dimensional array (or just a flat file) mapping new to old UID and then just spider the mount points in question. Just make sure you heavily test it prior to unlessing it.
I can offer you the following "multichown" Perl script that will do this sort of thing. It does rely on the filing system it is munging over to be static, and it has only been tested under Solaris.
#!/bin/perl
# This program does selective chown/chgrp operations on all inodes # in one or more directory trees. It will often be run as "root".
# There should be one or more option arguments of the form # -owner:NAME1:NAME2 (-o, -user, -u are synonyms of -owner) # -group:NAME1:NAME2 (-g is a synonym of -group) # to specify changing owner or group from NAME1 to NAME2. Users # and groups can be specified by name or by numeric uid/gid.
# The remaining arguments are the path names of the top-level directories # to be searched. (This directory itself is not included in the list of # inodes processed.) Relative path names are allowed provided the PWD # environment variable is set correctly on entry.
%OWNER = (); %GROUP = (); $OWNER = "user" ; $GROUP = "group"; %keys = ( "-owner", *OWNER, "-o", *OWNER, "-user", *OWNER, "-u", *OWNER, "-group", *GROUP, "-g", *GROUP);
while ($ARGV[0] =~ /^-/) { (@name = split(/:/,($arg = shift @ARGV),4)) == 3 && defined ($name = $keys{shift @name}) || die "$0: Invalid option $arg\n"; local(*KTYPE) = $name; for (@name) { next if /^\d+$/; defined($name = &KTYPE) || die "$0: Unknown $KTYPE $_\n"; $_ = $name; } $KTYPE{0+$name[0]} = 1+$name[1]; }
sub OWNER { return scalar getpwnam($_); } sub GROUP { return scalar getgrnam($_); }
keys (%OWNER) + keys (%GROUP) or die "$0: No owner or group substitutions specified\n";
# Prefix remaining arguments with $PWD if appropriate.
undef $prefix; for (@ARGV) { unless (m#^/#) { $prefix = &pwdpath."/" unless $prefix; $_ = $prefix.$_; } }
sub pwdpath { local($pwd) = $ENV{"PWD"}; local($deva,$inoa,$devb,$inob); (($deva,$inoa) = stat(".")) || die "$0: Cannot stat current directory: $!\n"; $pwd =~ m#^/# || die "$0: Invalid environment variable PWD = $pwd\n"; (($devb,$inob) = stat($pwd)) || die "$0: Cannot stat $pwd: $!\n"; ($deva==$devb && $inoa==$inob) || die "$0: Incorrect environment variable PWD = $pwd\n"; return $pwd; }
# Now do the real work passing over the directories.
while (defined ($name = shift @ARGV)) { unless (chdir $name) { warn "Cannot make $name current: $!\n" ; next } unless (opendir(DIR,".")) { warn "Cannot open $name: $!\n" ; next } while ($entry = readdir DIR) { next if $entry eq "." || $entry eq ".."; unless (@stat = lstat $entry) { warn "Unable to stat $name/$entry: $!\n" ; next } $perm = $stat[2]; $type = $perm&0xf000; if ($type==0x4000) { unshift (@ARGV,"$name/$entry"); } $olduid = $stat[4]; $newuid = $OWNER{$olduid}-1; $oldgid = $stat[5]; $newgid = $GROUP{$oldgid}-1; unless ($newuid < 0 && $newgid < 0) { if ($type==0x8000 && ($perm&06000)!=0) { warn "$name/$entry is setuid or setgid - not changed\n"; } elsif ($type==0xa000) { warn "$name/$entry is symlink - not changed\n" } elsif (chown ($newuid, $newgid, $entry)) { printf "%6d %6d -> %6d %6d ",$olduid,$oldgid,$newuid,$newgid; print "$name/$entry\n"; } else { warn "Unable to chown $name/$entry: $!\n" } } } closedir(DIR) }
# End of program