Sys-Export
view release on metacpan or search on metacpan
lib/Sys/Export/Unix/WriteFS.pm view on Meta::CPAN
my $dst_abs= $self->dst_abs . $file->{name};
length $file->{data}
or croak "Missing symlink contents for $file->{name}";
symlink($file->{data}, $dst_abs)
or croak "symlink($file->{data}, $dst_abs): $!";
$self->_apply_stat($dst_abs, $file);
}
# Install a device node into ->dst
sub _add_devnode($self, $file) {
if (defined $file->{rdev} && (!defined $file->{rdev_major} || !defined $file->{rdev_minor})) {
my ($major,$minor)= Sys::Export::Unix::_dev_major_minor($file->{rdev});
$file->{rdev_major} //= $major;
$file->{rdev_minor} //= $minor;
}
my $dst_abs= $self->dst_abs . $file->{name};
Sys::Export::Unix::_mknod_or_die($dst_abs, $file->{mode}, $file->{rdev_major}, $file->{rdev_minor});
$self->_apply_stat($dst_abs, $file);
}
# Install a fifo into ->dst
sub _add_fifo($self, $file) {
require POSIX;
my $dst_abs= $self->dst_abs . $file->{name};
POSIX::mkfifo($dst_abs, $file->{mode})
or croak "mkfifo($dst_abs): $!";
$self->_apply_stat($dst_abs, $file);
}
# Bind a socket (thus creating it) in ->dst
sub _add_socket($self, $file) {
require Socket;
my $dst_abs= $self->dst_abs . $file->{name};
socket(my $s, Socket::AF_UNIX(), Socket::SOCK_STREAM(), 0) or die "socket: $!";
bind($s, Socket::pack_sockaddr_un($dst_abs)) or die "Failed to bind socket at $dst_abs: $!";
$self->_apply_stat($dst_abs, $file);
}
sub finish($self) {
my $todo= delete $self->{_delayed_apply_stat};
# Reverse sort causes child directories to be updated before parents,
# which is required for updating mtimes.
$self->_delayed_apply_stat(@$_)
for sort { $b->[0] cmp $a->[0] } @$todo;
# free the temp directory if it was located within /dst_abs
undef $self->{tmp};
}
# Apply permissions and mtime to a path
sub _apply_stat($self, $abs_path, $stat) {
my ($mode, $uid, $gid, $atime, $mtime)= (lstat $abs_path)[2,4,5,8,9]
or croak "Failed to stat file just created at '$abs_path': $!";
my $change_uid= defined $stat->{uid} && $stat->{uid} != $uid;
my $change_gid= defined $stat->{gid} && $stat->{gid} != $gid;
if ($change_uid || $change_gid) {
# only UID 0 can change UID, and only GID 0 or GID in supplemental groups can change GID.
$uid= -1 unless $change_uid && $> == 0;
$gid= -1 unless $change_gid && ($) == 0 || grep $stat->{gid}, split / /, $) );
# Only attempt change if able
POSIX::lchown($uid, $gid, $abs_path) or croak "lchown($uid, $gid, $abs_path): $!"
if $uid >= 0 || $gid >= 0;
}
my @delayed;
# Don't change permission bits on symlinks
if (!S_ISLNK($mode) && ($mode & 0xFFF) != ($stat->{mode} & 0xFFF)) {
# If changing permissions on a directory to something that removes our ability
# to write to it, delay this change until the end.
if (S_ISDIR($mode) && !(($stat->{mode} & 0222) && ($stat->{mode} & 0111))) {
push @delayed, 'chmod';
}
else {
chmod $stat->{mode}&0xFFF, $abs_path
or croak sprintf "chmod(0%o, %s): $!", $stat->{mode}&0xFFF, $abs_path;
}
}
if (!S_ISLNK($mode) && (defined $stat->{mtime} || defined $stat->{atime})) {
if (S_ISDIR($mode)) {
# No point in applying mtime to a directory now, because it will get
# changed when sub-entries get written.
push @delayed, 'utime';
}
else {
utime $stat->{atime}, $stat->{mtime}, $abs_path
or warn "utime($abs_path): $!";
}
}
push @{$self->{_delayed_apply_stat}}, [ $abs_path, $stat, @delayed ]
if @delayed;
}
sub _delayed_apply_stat($self, $abs_path, $stat, @delayed) {
if (grep $_ eq 'chmod', @delayed) {
chmod $stat->{mode}&0xFFF, $abs_path
or croak sprintf "chmod(0%o, %s): $!", $stat->{mode}&0xFFF, $abs_path;
}
if (grep $_ eq 'utime', @delayed) {
utime $stat->{atime}, $stat->{mtime}, $abs_path
or warn "utime($abs_path): $!";
}
}
# Avoiding dependency on namespace::clean
delete @{Sys::Export::Unix::WriteFS::}{qw(
carp croak abs_path blessed isa_hash
S_IFBLK S_IFCHR S_IFDIR S_IFIFO S_IFLNK S_IFMT S_IFREG S_IFSOCK S_IFWHT
S_ISBLK S_ISCHR S_ISDIR S_ISFIFO S_ISLNK S_ISREG S_ISSOCK S_ISWHT
)};
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
( run in 0.612 second using v1.01-cache-2.11-cpan-5511b514fd6 )