CDS
view release on metacpan or search on metacpan
my $folder = shift;
# Create the folders to here if necessary
for my $intermediateFolder (CDS->intermediateFolders($folder)) {
mkdir $intermediateFolder, 0755;
}
# mkdir (if it does not exist yet) and chmod (if it does exist already)
mkdir $folder, $o->{permissions}->baseFolderMode;
chmod $o->{permissions}->baseFolderMode, $folder;
chown $o->{permissions}->uid // -1, $o->{permissions}->gid // -1, $folder;
# Check if the result is correct
my @s = stat $folder;
return $o->{ui}->error('Unable to create ', $o->{foldername}, '.') if ! scalar @s;
my $mode = $s[2];
return $o->{ui}->error($folder, ' exists, but is not a folder') if ! Fcntl::S_ISDIR($mode);
return $o->{ui}->error('Unable to set the owning user ', $o->{permissions}->user, ' for ', $folder, '.') if defined $o->{permissions}->uid && $s[4] != $o->{permissions}->uid;
return $o->{ui}->error('Unable to set the owning group ', $o->{permissions}->group, ' for ', $folder, '.') if defined $o->{permissions}->gid && $s[5] != $o->{permissions}->gid;
return $o->{ui}->error('Unable to set the mode on ', $folder, '.') if ($mode & 0777) != $o->{permissions}->baseFolderMode;
return 1;
}
sub existingFolderStoreOrShowError {
my $o = shift;
my $store = $o->{store} // $o->{actor}->preferredStore;
my $folderStore = CDS::FolderStore->forUrl($store->url);
}
sub traversalFailed {
my $o = shift;
my $folderStore = shift;
$o->{ui}->space;
$o->{ui}->p('Traversal failed because a file or folder could not be accessed. You may have to fix the permissions manually, or run this command with other privileges.');
$o->{ui}->p('If you have root privileges, you can take over this store using:');
my $userName = getpwuid($<);
my $groupName = getgrgid($();
$o->{ui}->line($o->{ui}->gold(' sudo chown -R ', $userName, ':', $groupName, ' ', $folderStore->folder));
$o->{ui}->p('and then set the desired permission scheme:');
$o->{ui}->line($o->{ui}->gold(' cds set permissions of ', $folderStore->folder, ' to â¦'));
$o->{ui}->space;
exit(1);
}
sub addAccount {
my $o = shift;
my $cmd = shift;
sub correct {
my $o = shift;
$o->{correct} += 1;
}
sub wrong {
my $o = shift;
my $item = shift;
my $uid = shift;
my $gid = shift;
my $mode = shift;
my $expectedUid = shift;
my $expectedGid = shift;
my $expectedMode = shift;
my $len = length $o->{baseFolder};
$o->{wrong} += 1;
$item = 'â¦'.substr($item, $len) if length $item > $len && substr($item, 0, $len) eq $o->{baseFolder};
my @changes;
push @changes, 'user '.&username($uid).' -> '.&username($expectedUid) if defined $expectedUid && $uid != $expectedUid;
push @changes, 'group '.&groupname($gid).' -> '.&groupname($expectedGid) if defined $expectedGid && $gid != $expectedGid;
push @changes, 'mode '.sprintf('%04o -> %04o', $mode, $expectedMode) if $mode != $expectedMode;
return $o->finalizeWrong(join(', ', @changes), "\t", $item);
}
sub username {
my $uid = shift;
return getpwuid($uid) // $uid;
}
sub groupname {
my $gid = shift;
return getgrgid($gid) // $gid;
}
sub accessError {
my $o = shift;
my $item = shift;
$o->{ui}->error('Error accessing ', $item, '.');
return 0;
}
$permissions->checkPermissions($subFolder.'/'.$file, $fileMode, $logger) || return;
}
}
return 1;
}
# Handles POSIX permissions (user, group, and mode).
package CDS::FolderStore::PosixPermissions;
# Returns the permissions set corresponding to the mode, uid, and gid of the base folder.
# If the permissions are ambiguous, the more restrictive set is chosen.
sub forFolder {
my $class = shift;
my $folder = shift;
my @s = stat $folder;
my $mode = $s[2] // 0;
return
($mode & 077) == 077 ? CDS::FolderStore::PosixPermissions::World->new :
($mode & 070) == 070 ? CDS::FolderStore::PosixPermissions::Group->new($s[5]) :
CDS::FolderStore::PosixPermissions::User->new($s[4]);
}
sub uid { shift->{uid} }
sub gid { shift->{gid} }
sub user {
my $o = shift;
my $uid = $o->{uid} // return;
return getpwuid($uid) // $uid;
}
sub group {
my $o = shift;
my $gid = $o->{gid} // return;
return getgrgid($gid) // $gid;
}
sub writeTemporaryFile {
my $o = shift;
my $folder = shift;
my $mode = shift;
# Write the file
my $temporaryFile = $folder.'/.'.CDS->randomHex(16);
open(my $fh, '>:bytes', $temporaryFile) || return;
print $fh @_;
close $fh;
# Set the permissions
chmod $mode, $temporaryFile;
my $uid = $o->uid;
my $gid = $o->gid;
chown $uid // -1, $gid // -1, $temporaryFile if defined $uid && $uid != $< || defined $gid && $gid != $(;
return $temporaryFile;
}
sub mkdir {
my $o = shift;
my $folder = shift;
my $mode = shift;
return if -d $folder;
# Create the folder (note: mode is altered by umask)
my $success = mkdir $folder, $mode;
# Set the permissions
chmod $mode, $folder;
my $uid = $o->uid;
my $gid = $o->gid;
chown $uid // -1, $gid // -1, $folder if defined $uid && $uid != $< || defined $gid && $gid != $(;
return $success;
}
# Check the permissions of a file or folder, and fix them if desired.
# A logger object is called for the different cases (access error, correct permissions, wrong permissions, error fixing permissions).
sub checkPermissions {
my $o = shift;
my $item = shift;
my $expectedMode = shift;
my $logger = shift;
my $expectedUid = $o->uid;
my $expectedGid = $o->gid;
# Stat the item
my @s = stat $item;
return $logger->accessError($item) if ! scalar @s;
my $mode = $s[2] & 07777;
my $uid = $s[4];
my $gid = $s[5];
# Check
my $wrongUid = defined $expectedUid && $uid != $expectedUid;
my $wrongGid = defined $expectedGid && $gid != $expectedGid;
my $wrongMode = $mode != $expectedMode;
if ($wrongUid || $wrongGid || $wrongMode) {
# Something is wrong
$logger->wrong($item, $uid, $gid, $mode, $expectedUid, $expectedGid, $expectedMode) || return 1;
# Fix uid and gid
if ($wrongUid || $wrongGid) {
my $count = chown $expectedUid // -1, $expectedGid // -1, $item;
return $logger->setError($item) if $count < 1;
}
# Fix mode
if ($wrongMode) {
my $count = chmod $expectedMode, $item;
return $logger->setError($item) if $count < 1;
}
} else {
# Everything is OK
$logger->correct($item, $mode, $uid, $gid);
}
return 1;
}
# The store belongs to a group. Every user belonging to the group is treated equivalent, and users are supposed to trust each other to some extent.
# The resulting store will have files belonging to multiple users, but the same group.
package CDS::FolderStore::PosixPermissions::Group;
use parent -norequire, 'CDS::FolderStore::PosixPermissions';
sub new {
my $class = shift;
my $gid = shift;
return bless {gid => $gid // $(};
}
sub target {
my $o = shift;
'members of the group '.$o->group }
sub baseFolderMode { 0771 }
sub objectFolderMode { 0771 }
sub objectFileMode { 0664 }
sub accountFolderMode { 0771 }
sub boxFolderMode {
( run in 3.369 seconds using v1.01-cache-2.11-cpan-5735350b133 )