#!/bin/sh # # Copyright (c) 2000 Carnegie Mellon University. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # 3. The name "Carnegie Mellon University" must not be used to # endorse or promote products derived from this software without # prior written permission. For permission or any other legal # details, please contact # Office of Technology Transfer # Carnegie Mellon University # 5000 Forbes Avenue # Pittsburgh, PA 15213-3890 # (412) 268-4387, fax: (412) 268-7395 # tech-transfer@andrew.cmu.edu # # 4. Redistributions of any form whatsoever must retain the following # acknowledgment: # "This product includes software developed by Computing Services # at Carnegie Mellon University (http://www.cmu.edu/computing/)." # # CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE # FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN # AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING # OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # exec perl -x -S $0 ${1+"$@"} # -*-perl-*- #!perl -w # script to upgrade from simple hashing scheme to full hashing scheme # make sure you run it as the cyrus user # # Written by Gary Mills # # $Id: rehash,v 1.7 2003/10/22 18:50:32 rjs3 Exp $ if ($] !~ /^5\..*/) { # uh-oh. this isn't perl 5. foreach (split(/:/, $ENV{PATH})) { # try to find "perl5". exec("$_/perl5", "-x", "-S", $0, @ARGV) if (-x "$_/perl5"); } # we failed. bail. die "Your perl is too old; I need perl 5.\n"; } # load the real script. this is isolated in an 'eval' so perl4 won't # choke on the perl5-isms. eval join("\n", ); if ($@) { die "$@"; } __END__ require 5; $| = 1; die "must not run as root" if ($< == 0); if ("-i" eq $ARGV[0]) { $interactive = 1; shift @ARGV; } if ("-f" eq $ARGV[0]) { $force = 1; shift @ARGV; } if ("-h" eq $ARGV[0] || @ARGV < 1) { print "usage: rehash [-i] [-f] none|basic|full [imapd.conf]\n"; print " -i interactive\n"; print " -f keep going on errors\n"; exit; } $MOVE_DOMAIN_CONF = 1; $MOVE_DOMAIN_SIEVE = 2; $MOVE_DOMAIN_PART = 3; $tonone = 1 if ("none" eq $ARGV[0]); $tobasic = 1 if ("basic" eq $ARGV[0]); $tofull = 1 if ("full" eq $ARGV[0]); unless ($tonone || $tobasic || $tofull) { print "rehash: one of none/basic/full required\n"; exit; } shift @ARGV; my @bdirs = ("a".."z"); my @fdirs = ("A".."W"); my $dirs = [@bdirs,@fdirs] if $tonone; if ($tobasic) { $dirs = \@bdirs; $old = \@fdirs; } if ($tofull) { $dirs = \@fdirs; $old = \@bdirs; } sub ouch { my $msg = shift; if ($force) { print "fatal error: $msg\n"; } else { print "error: $msg\n"; exit 1; } } sub dir_hash_c { my $name = shift; my ($h, $n); if ($tofull) { $n = 0; foreach my $b (split(/ */, $name)) { $n = (($n << 3) ^ ($n >> 5)) ^ ord($b); } $h = chr(ord('A') + ($n % 23)); return $h; } elsif ($tobasic) { $h = lc(substr($name, 0, 1)); if (!($h =~ /[a-z]/)) { $h = 'q'; } return $h; } } $imapdconf = shift || "/etc/imapd.conf"; $yn = "y"; $sievedir = "/usr/sieve"; $nosievedir = 0; $hashispool = 0; $virtdomains = 0; open CONF, $imapdconf or die "can't open $imapdconf"; while () { if (/^configdirectory:\s*(.*)$/) { $conf = $1; } if (/^sieveusehomedir:\s+(1|t|yes|on)/) { $nosievedir = 1; print "you are storing sieve scripts in user's home directories.\n"; } if (/^sievedir:\s+(.*)$/) { $sievedir = $1; print "you are using $sievedir as your sieve directory.\n"; } if (/^partition-.*:\s*(.*)$/) { if (grep /$1/, @parts) { next; } push @parts, $1; } if (/^hashimapspool:\s*(1|t|yes|on)/) { $hashispool = 1; print "i will also hash partitions.\n"; } if (/^virtdomains:\s+(1|t|yes|on)/) { $virtdomains = 1; print "i will deal with virtual domains.\n"; } } close CONF; if (! $conf) { $conf = "/var/imap"; } if ($interactive) { print "upgrade $conf? "; $yn = ; } if ($yn =~ /^y/) { unless (-d $conf) { print "creating $conf...\n"; mkdir $conf, 0755; } print "converting configuration directory $conf..."; chdir $conf or die "couldn't change to $conf"; foreach $i ("user", "proc", "db", "socket", "log", "msg", "quota") { unless (-d $i) { print "creating $i...\n"; mkdir $i, 0755; } } # *** rehash the domain subdirectory to the new format, # don't worry about internal format yet if($virtdomains) { print "domain "; chdir "domain" or die "couldn't change to domain subdir"; &move_domains($MOVE_DOMAIN_CONF); chdir ".."; } # *** user subdirectory; holds subscription files print "user "; chdir "user" or die "couldn't change to user subdir"; &move_users; chdir ".."; # *** quota subdirectory; holds quota files for each quotaroot print "quota "; chdir "quota" or die "couldn't change to quota subdir"; &move_quotas; print "done\n"; } # create the sieve stuff unless ($nosievedir) { print "converting $sievedir...\n"; mkdir $sievedir, 0755; if (chdir $sievedir) { &move_sieve; } } # *** now for each data partition my $i; my $f; while ($part = shift @parts) { if ($interactive) { print "upgrade $part? "; $yn = ; } if ($yn =~ /^y/) { unless (-d $part) { print "creating $part...\n"; mkdir $part, 0755; } print "converting data partition $part..."; chdir $part or die "couldn't chdir to $part"; if ($hashispool) { &move_part; chdir $part or die "couldn't chdir to $part"; mkdir "stage.", 0755; } print "done\n"; } } sub do_subdomain_conf { if(-d "quota") { chdir "quota"; &move_quotas; chdir ".."; } if(-d "user") { chdir "user"; &move_users; chdir ".."; } } sub move_domains { my $type_of_move = shift; if(!defined($type_of_move) || !$type_of_move) { die "move_domains called badly"; } my $i; my $s; my $h; my $mbox; foreach $i (@{$dirs}) { if ($tonone) { if (opendir SUB, $i) { while ($s = readdir SUB) { if ($s =~ /^\./s) { next; } chdir "$i/$s"; &do_subdomain; chdir "../.."; rename("$i/$s", "$s") or die "couldn't move $s back!"; } closedir SUB; rmdir "$i" or die "couldn't remove $i"; } } else { unless (-d $i) { mkdir ("$i", 0755) or ouch "couldn't create $i"; } } } unless ($tonone) { foreach $i (@{$old}) { if (opendir SUB, $i) { while ($s = readdir SUB) { if ($s =~ /^\./s) { next; } chdir "$i/$s"; if($type_of_move == $MOVE_DOMAIN_CONF) { &do_subdomain_conf; } elsif ($type_of_move == $MOVE_DOMAIN_SIEVE) { &move_sieve; } elsif ($type_of_move == $MOVE_DOMAIN_PART) { &move_part; } else { die "bad domain move mode: $type_of_move"; } chdir "../.."; $h = dir_hash_c($s); print "moving $i/$s to $h/$s\n"; rename("$i/$s", "$h/$s") or ouch "couldn't move $s back!"; } closedir SUB; rmdir "$i" or die "couldn't remove $i"; } } opendir (USER, "."); while ($f = readdir USER) { if ($f =~ /^\./s) { next; } # don't move the hashed directories themselves my $flag = 0; foreach $item (@{$dirs}) { if($item eq $f) { $flag = 1; $break; } } next if($flag); # hash on name before '.sub' suffix print "$f\n"; $h = dir_hash_c($f); rename ($f, "$h/$f") or ouch "couldn't move $f into $h"; } closedir USER; } } sub move_users { my $i; my $s; my $h; my $f; my $mbox; foreach $i (@{$dirs}) { if ($tonone) { if (opendir SUB, $i) { while ($s = readdir SUB) { if ($s =~ /^\./s) { next; } rename("$i/$s", "$s") or die "couldn't move $s back!"; } closedir SUB; rmdir "$i" or die "couldn't remove $i"; } } else { unless (-d $i) { mkdir ("$i", 0755) or ouch "couldn't create $i"; } } } unless ($tonone) { foreach $i (@{$old}) { if (opendir SUB, $i) { while ($s = readdir SUB) { if ($s =~ /^\./s) { next; } # hash on name before '.sub' suffix if ($s =~ /^(.+)\./) { $h = dir_hash_c($1); rename("$i/$s", "$h/$s") or ouch "couldn't move $s back!"; } } closedir SUB; rmdir "$i" or die "couldn't remove $i"; } } opendir (USER, "."); while ($f = readdir USER) { if ($f =~ /^\./s) { next; } # hash on name before '.sub' suffix if ($f =~ /^(.+)\./) { print "$f\n"; $h = dir_hash_c($1); rename ($f, "$h/$f") or ouch "couldn't move $f into $h"; } } closedir USER; } } sub move_quotas { my $i; my $s; my $h; my $mbox; # first, create directories we know can't conflict with existing files foreach $i (@{$dirs}) { if ($tonone) { if (-d $i) { rename ($i, ".$i") or die "couldn't rename $i to .$i"; opendir SUB, ".$i"; while ($s = readdir SUB) { if ($s =~ /^\./s) { next; } rename(".$i/$s", $s) or die "couldn't move $s back!"; } closedir SUB; rmdir ".$i" or die "couldn't remove .$i"; } } else { if (-d $i) { rename ($i, ".$i") or die "couldn't rename $i to .$i"; } else { mkdir (".$i", 0755); } } } # now for each file, move it into the appropriate directory unless ($tonone) { foreach $i (@{$old}) { if (opendir SUB, $i) { while ($s = readdir SUB) { # hash on name after 'user.' if ($s =~ /^.+\.(.+)$/) { $h = dir_hash_c($1); rename("$i/$s", ".$h/$s") or ouch "couldn't move $s back!"; } } closedir SUB; rmdir "$i" or die "couldn't remove $i"; } } opendir QUOTA, "."; while ($mbox = readdir QUOTA) { if ($mbox =~ /^\./s) { next; } # hash on name after 'user.' if ($mbox =~ /^.*\.(.*)$/) { $h = dir_hash_c($1); rename($mbox, ".$h/$mbox") or ouch "couldn't move $mbox into $h"; next; } # we should try to hash the entire file $h = dir_hash_c($mbox); rename($mbox, ".$h/$mbox") or ouch "couldn't move $mbox into $h"; next; } closedir QUOTA; # now move each temporary directory to the right place foreach $i (@{$dirs}) { rename (".$i", $i) or ouch "couldn't rename $i into place"; } } } sub move_sieve { my $i; my $s; my $h; my $mbox; foreach $i (@{$dirs}) { unless ($tonone) { if (-d $i) { rename ($i, ".$i") or die "couldn't rename $i to .$i"; } else { mkdir (".$i", 0755); } } else { rmdir "$i"; } } unless ($tonone) { foreach $i (@{$old}) { if (opendir SUB, $i) { while ($s = readdir SUB) { unless ($s =~ /^\./) { $h = dir_hash_c($s); rename("$i/$s", ".$h/$s") or ouch "couldn't move $s back!"; } } closedir SUB; rmdir "$i" or die "couldn't remove $i"; } } # now move each temporary directory to the right place foreach $i (@{$dirs}) { rename (".$i", $i) or ouch "couldn't rename $i into place"; } } if($virtdomains && chdir "domain") { &move_domains($MOVE_DOMAIN_SIEVE); chdir ".."; } } sub move_part { my $i; my $s; my $t; my $h; my $dir; my $sub; my $ismbox; foreach $i (@{$dirs}) { if ($tonone) { if (-d $i) { rename ($i, ".$i") or die "couldn't rename $i to .$i"; print "$i "; opendir SUB, ".$i"; while ($s = readdir SUB) { if ($s =~ /^\./s) { next; } mkdir $s, 0755; # ignore errors as it might already exist opendir MV, ".$i/$s"; while ($t = readdir MV) { if ($t =~ /^\./s) { next; } rename (".$i/$s/$t", "$s/$t") or die "couldn't rename .$i/$s/$t to $s/$t"; } closedir MV; } closedir SUB; rmdir ".$i" or die "could not remove .$i"; } print "done\n"; } else { mkdir (".$i", 0755) or ouch "couldn't create .$i"; } } unless ($tonone) { foreach $i (@{$old}) { if (opendir SUB, $i) { while ($dir = readdir SUB) { if ($dir =~ /^\./s) { next; } # process $dir print "$i/$dir "; opendir DIR, "$i/$dir"; $ismbox = 0; while ($sub = readdir DIR) { if ($sub =~ /^\./s) { next; } # if there's a dot in this, we're a mbox and # this isn't a child if ($sub =~ /(.*)\.(.*)/) { $ismbox = 1; next; } print "/$sub "; $h = dir_hash_c($sub); mkdir (".$h/$dir", 0755); # might already be there rename("$i/$dir/$sub", ".$h/$dir/$sub") or ouch "couldn't move $dir/$sub into $h"; } closedir DIR; # if $ismbox is set, then $dir is a mailbox of it's own right if ($ismbox) { $h = dir_hash_c($dir); mkdir (".$h/$dir", 0755); # might already be there opendir DIR, "$i/$dir"; while ($sub = readdir DIR) { if ($sub =~ /^\./s) { next; } print "/$sub "; rename("$i/$dir/$sub", ".$h/$dir/$sub") or ouch "couldn't move $dir into $h"; } closedir DIR; } rmdir "$i/$dir" or print "\ncouldn't remove '$dir'??\n"; } closedir SUB; rmdir "$i" or die "couldn't remove $i"; } } opendir PART, "."; while ($dir = readdir PART) { if ($dir =~ /^\./s) { next; } if ($dir eq "lost+found") { next; } if ($dir eq "stage.") { next; } if ($dir eq "domain") { if(chdir "domain") { &move_domains($MOVE_DOMAIN_PART); chdir ".."; } next; } # process $dir print "$dir "; opendir DIR, $dir; $ismbox = 0; while ($sub = readdir DIR) { if ($sub =~ /^\./s) { next; } # if there's a dot in this, we're a mbox and # this isn't a child if ($sub =~ /(.*)\.(.*)/) { $ismbox = 1; next; } $h = dir_hash_c($sub); mkdir (".$h/$dir", 0755); # might already be there rename("$dir/$sub", ".$h/$dir/$sub") or ouch "couldn't move $dir/$sub into $h"; } closedir DIR; # if $ismbox is set, then $dir is a mailbox of it's own right if ($ismbox) { $h = dir_hash_c($dir); mkdir (".$h/$dir", 0755); # might already be there opendir DIR, $dir; while ($sub = readdir DIR) { if ($sub =~ /^\./s) { next; } rename("$dir/$sub", ".$h/$dir/$sub") or ouch "couldn't move $dir into $h"; } closedir DIR; } rmdir $dir or print "\ncouldn't remove '$dir'??\n"; } closedir PART; foreach $i (@{$dirs}) { rename (".$i", $i) or ouch "couldn't rename .$i to $i"; } } }