Упрощенная идея
Когда я упоминал о потоке, я не имел в виду неконтролируемый . Журнал базы данных (например) также записывается в потоковом режиме для сравнения.
Также обратите внимание, что утверждение о том, что вы не можете позволить себе сортировать даже один подкаталог , прямо противоречит использованию Perl-хеша для хранения той же информации (я не буду винить вас, если вы не не имеет фона CS).
Итак, вот очень простая иллюстрация того, что вы могли бы сделать. Обратите внимание, что каждый шаг на пути является потоковым, повторяемым и регистрируется.
# export SOME_FIND_OPTIONS=...?
find $SOME_FIND_OPTIONS -print0 | ./generate_script.pl > chownscript.sh
# and then
sh -e ./chownscript.sh
Пример generate_script.pl (очевидно, адаптируйте его под свои нужды:)
#!/usr/bin/perl
use strict;
use warnings;
$/="\0";
while (<>)
{
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat;
# demo purpose, silly translation:
my ($newuid, $newgid) = ($uid+1000, $gid+1000);
print "./chmod.pl $uid:$gid $newuid:$newgid '$_'\n"
}
У вас может быть системно-зависимая реализация chmod.pl (это помогает уменьшить сложность и, следовательно,: риск):
#!/usr/bin/perl
use strict;
use warnings;
my $oldown = shift;
my $newown = shift;
my $path = shift;
($oldown and $newown and $path) or die "usage: $0 <uid:gid> <newuid:newgid> <path>";
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat $path;
die "file not found: $path" unless $ino;
die "precondition failed" unless ($oldown eq "$uid:$gid");
($uid, $gid) = split /:/, $newown;
chown $uid, $gid, $path or die "unable to chown: $path"
Это позволит вам перезапускаться, когда что-то происходит на полпути, и даже позволит вам вручную выбирать исключения, если это необходимо. Вы можете сохранить сценарии, чтобы иметь подотчетность. Я сделал разумный удар по обеспечению безопасной работы скриптов. Тем не менее, это, очевидно, только отправная точка. Самое главное, я не имею дело с пересечениями файловой системы, символическими ссылками, сокетами, узлами устройств, где вы можете обратить на них внимание.
оригинальный ответ следует:
Идеи
Да, если проблема в производительности, сделайте это в C
Не ведите постоянное ведение журнала для всей файловой системы (кстати, зачем вам нужно хранить их в одном хеше? Потоковый вывод - ваш друг)
Вместо этого регистрируйте выполненные запуски для каждого каталога. Вы можете легко разбить отображение по шагам:
user A: 1 -> 99
user B: 2 -> 1
user A: 99 -> 2
Ownify - что я использую ( код )
До тех пор, пока вы можете зарезервировать диапазон для временных uid / guids, таких как 99, не будет никакого риска необходимости перезапуска (не больше, чем * , чем выполнять эту транснумерацию в действующей файловой системе, в любом случае). ).
Вы могли бы начать с этого приятного небольшого фрагмента кода C (который, по общему признанию, не очень оптимистично):
// vim: se ts=4 sw=4 et ar aw
//
// make: g++ -D_FILE_OFFSET_BITS=64 ownify.cpp -o ownify
//
// Ownify: ownify -h
//
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
/* old habits die hard. can't stick to pure C ... */
#include <string>
#include <iostream>
#define do_stat(a,b) lstat(a,b)
#define do_chown(a,b,c) lchown(a,b,c)
//////////////////////////////////////////////////////////
// logic declarations
//
void ownify(struct stat& file)
{
// if (S_ISLNK(file.st_mode))
// return;
switch (file.st_uid)
{
#if defined(PASS1)
case 1: file.st_uid = 99; break;
case 99: fputs(err, "Unexpected existing owned file!"); exit(255);
#elif defined(PASS2)
case 2: file.st_uid = 1; break;
#elif defined(PASS3)
case 99: file.st_uid = 1; break;
#endif
}
switch (file.st_gid) // optionally map groups as well
{
#if defined(PASS1)
#elif defined(PASS2)
#elif defined(PASS3)
#endif
}
}
/////////////////////////////////////////////////////////
// driver
//
static unsigned int changed = 0, skipped = 0, failed = 0;
static bool dryrun = false;
void process(const char* const fname)
{
struct stat s;
if (0==do_stat(fname, &s))
{
struct stat n = s;
ownify(n);
if ((n.st_uid!=s.st_uid) || (n.st_gid!=s.st_gid))
{
if (dryrun || 0==do_chown(fname, n.st_uid, n.st_gid))
printf("%u\tchanging owner %i:%i '%s'\t(was %i:%i)\n",
++changed,
n.st_uid, n.st_gid,
fname,
s.st_uid, s.st_gid);
else
{
failed++;
int e = errno;
fprintf(stderr, "'%s': cannot change owner %i:%i (%s)\n",
fname,
n.st_uid, n.st_gid,
strerror(e));
}
}
else
skipped++;
} else
{
int e = errno;
fprintf(stderr, "'%s': cannot stat (%s)\n", fname, strerror(e));
failed++;
}
}
int main(int argc, char* argv[])
{
switch(argc)
{
case 0: //huh?
case 1: break;
case 2:
dryrun = 0==strcmp(argv[1],"-n") ||
0==strcmp(argv[1],"--dry-run");
if (dryrun)
break;
default:
std::cerr << "Illegal arguments" << std::endl;
std::cout <<
argv[0] << " (Ownify): efficient bulk adjust of owner user:group for many files\n\n"
"Goal: be flexible and a tiny bit fast\n\n"
"Synopsis:\n"
" find / -print0 | ./ownify -n 2>&1 | tee ownify.log\n\n"
"Input:\n"
" reads a null-delimited stream of filespecifications from the\n"
" standard input; links are _not_ dereferenced.\n\n"
"Options:\n"
" -n/--dry-run - test run (no changes)\n\n"
"Exit code:\n"
" number of failed items" << std::endl;
return 255;
}
std::string fname("/dev/null");
while (std::getline(std::cin, fname, '\0'))
process(fname.c_str());
fprintf(stderr, "%s: completed with %u skipped, %u changed and %u failed%s\n",
argv[0], skipped, changed, failed, dryrun?" (DRYRUN)":"");
return failed;
}
Обратите внимание, что для этого требуется немало мер безопасности
- проверка на паранойю при первом прохождении (проверьте, нет ли полей с зарезервированным uid)
- возможность изменять поведение
do_stat
и do_chown
относительно ссылок
- a
--dry-run
(чтобы посмотреть, что будет сделано ) -n
Программа с удовольствием расскажет вам, как ее использовать с ownify -h
:
./ownify (Ownify): efficient bulk adjust of owner user:group for many files
Goal: be flexible and a tiny bit fast
Synopsis:
find / -print0 | ./ownify -n 2>&1 | tee ownify.log
Input:
reads a null-delimited stream of file specifications from the
standard input;
Options:
-n/--dry-run - test run (no changes)
Exit code:
number of failed items