Article 2827 of comp.lang.perl:
Xref: feenix.metronet.com comp.lang.perl:2827
Newsgroups: comp.lang.perl
Path: feenix.metronet.com!news.utdallas.edu!tamsun.tamu.edu!cs.utexas.edu!asuvax!ncar!tres
From:
[email protected] (Tres Hofmeister)
Subject: Re: SOURCE: change UIDs thoughout your filesystems
Message-ID: <
[email protected]>
Sender:
[email protected] (USENET Maintenance)
Organization: NCAR, Research Applications Program
References: <
[email protected]>
Date: Mon, 17 May 1993 21:50:55 GMT
Lines: 286
In article <
[email protected]>
[email protected]
(Hal Pomeranz) writes:
> I had the fun of changing UIDs on my systems last week, so I wrote
> the following little proglet to descend through my directories and
> change ownerships everywhere. The script expects one argument which
> is the name of a file containing lines with the old UID and the UID
> that it's changing to, separated by whitespace (one pair per line).
> There's an optional second argument if you want to start somewhere
> other than the root. The script will not descend into NFS filesystems.
In article <
[email protected]>
[email protected]
(Tom Christiansen) writes:
> From the keyboard of
[email protected]:
> : P.S. How do I learn perl, where do I start?
>
> Read other people's code. Understand it. Copy it. Change it.
> --tom
With Tom's advice in mind, here's a modified version of his
`mvids' script, which performs the same function as Hal's, and more.
--
Tres Hofmeister
[email protected]
#!/local/bin/perl
# mvids: "moves" uids and gids
# Tom Christiansen <
[email protected]>
# Modified by: Tres Hofmeister <
[email protected]>
# 3/29/93
# Use find.pl and `lstat' rather than reading a pipe from find(1):
# For better portability, as not all finds support -fstype or -ls;
# indexing into the find string breaks on device special files and
# filenames containing whitespace; `split' was breaking up the
# string inconsistently because of leading whitespace (using
# split(' ') and changing the indexing could also fix this).
# Fixed the non-existent user/group tests, which were using the wrong
# array names;
# Allow negative uid/gid's.
# Delete temporary files if -n is specified.
# Changed the name of the default directions file to `mvids.cf'.
# Various minor or cosmetic changes, including addition of comments.
#
# Usage: mvids [-n] [-d] [-f dfile] [startdir]
#
# Takes a file of new user and group id's, creates and installs new
# passwd and group files, and traverses local filesystems starting with
# startdir (default is `/'), updating all changed id's.
#
# Read descriptions from `mvids.cf' file with format:
# type name number
# e.g.:
# user tom 1023
# group staff 200
#
# Options:
# -n Find files and create the new passwd and group files, but don't
# actually change anything.
# -f Specify an alternate description file than the default `mvids.cf'.
# -d Use the passwd and group files in the current directory.
require 'find.pl';
require 'getopts.pl';
$| = 1;
$oops = 0;
($prog = $0) =~ s#.*/##;
&Getopts('dnf:');
$FILE = $opt_f || "mvids.cf";
$DIR = $opt_d ? "." : "/etc";
$topdir = shift || '/';
die "Usage: $prog [-n] [-d] [-f dfile] [startdir]\n" if $#ARGV > -1;
die "$topdir: Not a directory" unless -d $topdir;
# Process directions file.
open FILE || die "Can't open directions file \"$FILE\": $!\n";
while (<FILE>) {
s/\s*#.*//;
next if /^$/;
unless (/^(user|group)\s+(\w+)\s+(\d+)/) {
print STDERR "malformed line at line $. of $FILE: $_";
$oops++; next;
}
if ($3 > 32767) {
print STDERR "$1 $2 has id that's too big ($3)\n";
$oops++; next;
}
if ($3 == 0) {
print STDERR "Too dangerous to move $1 $2 to 0\n";
$oops++; next;
}
if ($2 eq 'root') {
print STDERR "You don't really want to move root!\n";
$oops++; $next;
}
if ($1 eq 'user') {
if (defined $n_pwn2i{$2}) {
print STDERR "Saw user $2 again at line $. of $FILE\n";
$oops++; next;
}
if (defined $n_pwi2n{$3}) {
print STDERR "Saw uid $3 again at line $. of $FILE\n";
$oops++; next;
}
$uids++;
# Build %n_pwn2i and %n_pwi2n.
$n_pwn2i{$2} = $3;
$n_pwi2n{$3} = $2;
}
else {
if (defined $n_grn2i{$2}) {
print STDERR "Saw group $2 again at line $. of $FILE\n";
$oops++; next;
}
if (defined $n_gri2n{$3}) {
print STDERR "Saw gid $3 again at line $. of $FILE\n";
$oops++; next;
}
$gids++;
# Build %n_grn2i and %n_gri2n.
$n_grn2i{$2} = $3;
$n_gri2n{$3} = $2;
}
}
# Process the existing passwd file, build the new one.
if ($uids) {
$PWD = "$DIR/passwd";
$NPWD = "$DIR/passwd.new";
open PWD || die "Can't open $PWD: $!\n";
open (NPWD, ">$NPWD") || die "Can't create $NPWD: $!\n";
while (<PWD>) {
((($name, $uid) = /^(\w+):[^:]*:(-?[\d]+):/))
|| die "Bad passwd entry at line $.\n";
if (defined $n_pwi2n{$uid} && !defined $n_pwn2i{$name}) {
printf STDERR "Can't move user %s to uid %d, %s already has it\n",
$n_pwi2n{$uid}, $uid, $name;
$oops++;
next;
}
# Build %pwn2i.
$pwn2i{$name} = $uid;
# Edit the current line if necessary.
s/:$uid:/:$n_pwn2i{$name}:/ if defined $n_pwn2i{$name};
print NPWD;
}
close PWD;
close NPWD;
foreach $user (keys %n_pwn2i) {
unless (defined $pwn2i{$user}) {
print STDERR "Can't move non-existent user $user\n";
$oops++;
}
}
}
# Process the existing group file, build the new one.
if ($gids) {
$GRP = "$DIR/group";
$NGRP = "$DIR/group.new";
open GRP || die "Can't open $GRP: $!\n";
open (NGRP, ">$NGRP") || die "Can't create $NGRP: $!\n";
while (<GRP>) {
((($name, $gid) = /^(\w+):[^:]*:(-?[\d]+):/))
|| die "Bad group entry at line $.\n";
if (defined $n_gri2n{$gid} && !defined $n_grn2i{$name}) {
printf STDERR "Can't move gid %s to %d, %s already has it\n",
$n_gri2n{$gid}, $gid, $name;
$oops++;
next;
}
# Build %grn2i.
$grn2i{$name} = $gid;
# Edit the current line if necessary.
s/:$gid:/:$n_grn2i{$name}:/ if defined $n_grn2i{$name};
print NGRP;
}
close GRP;
close NGRP;
foreach $group (keys %n_grn2i) {
unless (defined $grn2i{$group}) {
print STDERR "Can't move non-existent group $group\n";
$oops++;
}
}
}
# Exit if there were errors processing files, or if there's nothing to do.
die "$prog: no ids to move\n" unless $uids || $gids;
die "$prog: $oops error" . ($oops > 1 ? "s" : "") .
" in remapping directions.\n" if $oops;
# Ok, now do it.
# Build %pwi2n from %pwn2i.
foreach $key (keys %pwn2i) {
$pwi2n{$pwn2i{$key}} = $key;
}
# Build %gri2n from %grn2i.
foreach $key (keys %grn2i) {
$gri2n{$grn2i{$key}} = $key;
}
&find("$topdir");
sub wanted {
# Called by &find. $name contains the current pathname,
# $_ contains the filename component of the pathname.
# Prune NFS filesystems.
unless ((($dev, $user, $group) = (lstat($_))[0,4,5])
&& $dev < 0 && ($prune = 1)) {
$uid = $gid = -1;
$file = $name;
# Convert numeric id's to names.
$user = $pwi2n{$user};
$group = $gri2n{$group};
# If this file is owned by a user to be changed...
if (defined $n_pwn2i{$user}) {
$uid = $n_pwn2i{$user};
print "changing owner $user of $file from ",
"$pwn2i{$user} to $n_pwn2i{$user}\n";
}
# If this file is in a group to be changed...
if (defined $n_grn2i{$group}) {
$gid = $n_grn2i{$group};
print "changing group $group of $file from ",
"$grn2i{$group} to $n_grn2i{$group}\n";
}
# Change the uid and/or gid of the file. If both are still -1,
# this file doesn't need changing. Passing chown -1 for the
# uid or gid leaves it unchanged.
if (!$opt_n && ($uid != -1 || $gid != -1)) {
if (!chown $uid, $gid, $_) {
print STDERR "couldn't chown $file to $uid.$gid: $!\n";
$oops++;
}
}
}
}
# Install the new passwd and group files...
unless ($opt_n) {
if ($uids) {
rename($PWD, "$PWD.bak")
|| die "Can't mv $PWD to $PWD.bak: $!\n";
rename($NPWD, $PWD)
|| die "Can't mv $NPWD to $PWD: $!\n";
}
if ($gids) {
rename($GRP, "$GRP.bak")
|| die "Can't mv $GRP to $GRP.bak: $!\n";
rename($NGRP, $GRP)
|| die "Can't mv $NGRP to $GRP: $!\n";
}
}
else {
# Clean up if we were just testing.
unlink("$NPWD", "$NGRP");
}
exit ($oops != 0);
--
Tres Hofmeister
[email protected]