Someone (sorry, deleted message) pointed out that perl's chown() call probably follows symlinks, and that is indeed the case. However, there is a very simple workaround for that:
find dir ! -type l -print | mychown.c
Also, as someone pointed out, if you use chown -R you also want to use -h (if your unix flavor supports that option) to change the owner of the symlink itself rather than what the symlink points to.
If you run a separate chown process for each file, yes, that will be very slow. However, if these files are all in a small number of directory trees, then just use the recursive option of chown.
chown -R newuser:newgrp dir
This chowns the entire tree and I can't think of anything on the client side that would be faster. I don't think ONTAP has anything on the server side to do this operation.
Of course this assumes that you want the entire directory tree chown-ed to the same user and group. If you need to pick and choose based on the existing uid (probably what you want) then I suggest doing something like this:
find dir -print | mychown.pl
And write a mychown.pl perl script like this:
===========
%uidmap = ( # in %uidmap each line is olduid => newuid 120 => 507, 153 => 515, ... ); $defaultuid = 507; # for when the old uid is not in %uidmap
%gidmap = ( # in %gidmap each line is oldgid => newgid 200 => 217, 201 => 234, ... ); $defaultgid = 212; # for when the old gid is not in %gidmap
while(<>) { chop; @stat = stat($_); next if @stat == 0; # stat call failed, move on $olduid = $stat[4]; $oldgid = $stat[5]; $newuid = exists($uidmap{$olduid}) ? $uidmap{$olduid} : $defaultuid; $newgid = exists($gidmap{$oldgid}) ? $gidmap{$oldgid} : $defaultgid; chown($newuid, $newgid, $_); } ===========
Steve Losen scl@virginia.edu phone: 804-924-0640
University of Virginia ITC Unix Support
Steve Losen scl@sasha.acc.virginia.edu writes
Someone (sorry, deleted message) pointed out that perl's chown() call probably follows symlinks, and that is indeed the case. However, there is a very simple workaround for that:
find dir ! -type l -print | mychown.c
Also, as someone pointed out, if you use chown -R you also want to use -h (if your unix flavor supports that option) to change the owner of the symlink itself rather than what the symlink points to.
Perl's chown will normally be using the underlying chown(2) system call, and whether that follows symlinks or not depends on your Unix flavour: e.g. it didn't in SunOS4, but it does in Solaris 2+.
With some trepidation, I include the "multichown" Perl scipt that I use to make systematic changes to uids and gids in directory trees. It's quite old so please excuse all the Perl4'isms (the globs, no my variables, etc.) It's specifically programmed to run under Solaris: tweaks to the $type values might well be required on other systems. I did find one bug in it while I was looking at today, which I have fixed. :) It just leaves out symlinks, but I know that the right things to do is to write a C extension to get access to Solaris' lchown(2) call instead.
With all the methods discussed so far, there's another potential problem. Unless the directory trees and their contents are guaranteed unchanging, havoc can arise if changes are made between the stat() and chown() calls.
Chris Thompson University of Cambridge Computing Service, Email: cet1@ucs.cam.ac.uk New Museums Site, Cambridge CB2 3QG, Phone: +44 1223 334715 United Kingdom.
=== multichown script begins ===
#!/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 "%5d %5d -> %5d %5d ",$olduid,$oldgid,$newuid,$newgid; print "$name/$entry\n"; } else { warn "Unable to chown $name/$entry: $!\n" } } } closedir(DIR) }
# End of program
=== multichown script ends ===
Chris Thompson University of Cambridge Computing Service, Email: cet1@ucs.cam.ac.uk New Museums Site, Cambridge CB2 3QG, Phone: +44 1223 334715 United Kingdom.