use
Fcntl
qw/:DEFAULT :mode/
;
our
@ISA
=
qw/Filesys::POSIX::Inode/
;
sub
new {
my
(
$class
,
$path
,
%opts
) =
@_
;
my
$sticky
= 0;
if
(
defined
$opts
{
'dev'
}->{
'sticky'
} ) {
$sticky
=
$opts
{
'dev'
}->{
'sticky'
} ? 1 : 0;
}
if
(
defined
$opts
{
'sticky'
} ) {
$sticky
=
$opts
{
'sticky'
} ? 1 : 0;
}
return
bless
{
'path'
=>
$path
,
'dev'
=>
$opts
{
'dev'
},
'parent'
=>
$opts
{
'parent'
},
'sticky'
=>
$sticky
,
'dirty'
=> 0
},
$class
;
}
sub
from_disk {
my
(
$class
,
$path
,
%opts
) =
@_
;
my
@st
=
$opts
{
'st_info'
} ? @{
$opts
{
'st_info'
} } :
lstat
$path
or confess($!);
my
$inode
=
$class
->new(
$path
,
%opts
)->update(
@st
);
if
( S_IFMT(
$st
[2] ) == S_IFDIR ) {
$inode
->{
'directory'
} = Filesys::POSIX::Real::Directory->new(
$path
,
$inode
);
}
return
$inode
;
}
sub
child {
my
(
$self
,
$name
,
$mode
) =
@_
;
my
$directory
=
$self
->directory;
confess(
'Invalid directory entry name'
)
if
$name
=~ /\//;
confess(
'File exists'
)
if
$directory
->
exists
(
$name
);
my
$path
=
"$self->{'path'}/$name"
;
my
%data
= (
'dev'
=>
$self
->{
'dev'
},
'sticky'
=>
$self
->{
'sticky'
},
'parent'
=>
$directory
->get(
'.'
)
);
if
( (
$mode
&
$S_IFMT
) ==
$S_IFREG
) {
sysopen
(
my
$fh
,
$path
, O_CREAT | O_EXCL | O_WRONLY,
$mode
) or confess($!);
close
(
$fh
);
}
elsif
( (
$mode
&
$S_IFMT
) ==
$S_IFDIR
) {
mkdir
(
$path
,
$mode
) or confess($!);
}
my
$inode
;
if
( (
$mode
&
$S_IFMT
) ==
$S_IFLNK
) {
confess(
'Operation not permitted'
)
unless
$self
->{
'sticky'
};
$inode
= Filesys::POSIX::Mem::Inode->new(
%data
,
'mode'
=>
$mode
);
}
else
{
$inode
= __PACKAGE__->from_disk(
$path
,
%data
);
}
return
$directory
->set(
$name
,
$inode
);
}
sub
taint {
my
(
$self
) =
@_
;
$self
->{
'dirty'
} = 1;
return
$self
;
}
sub
update {
my
(
$self
,
@st
) =
@_
;
if
(
$self
->{
'sticky'
} &&
$self
->{
'dirty'
} ) {
@{
$self
}{
qw/rdev size atime mtime ctime/
} =
@st
[ 6 .. 10 ];
}
else
{
$self
->SUPER::update(
@st
);
}
return
$self
;
}
sub
open
{
my
(
$self
,
$flags
) =
@_
;
sysopen
(
my
$fh
,
$self
->{
'path'
},
$flags
) or confess($!);
return
Filesys::POSIX::IO::Handle->new(
$fh
);
}
sub
chown
{
my
(
$self
,
$uid
,
$gid
) =
@_
;
unless
(
$self
->{
'sticky'
} ) {
CORE::
chown
(
$uid
,
$gid
,
$self
->{
'path'
} ) or confess($!);
}
@{
$self
}{
qw/uid gid/
} = (
$uid
,
$gid
);
return
$self
->taint;
}
sub
chmod
{
my
(
$self
,
$mode
) =
@_
;
my
$format
=
$self
->{
'mode'
} &
$S_IFMT
;
my
$perm
=
$mode
& (
$S_IPERM
|
$S_IPROT
);
unless
(
$self
->{
'sticky'
} ) {
CORE::
chmod
(
$perm
,
$self
->{
'path'
} ) or confess($!);
}
$self
->{
'mode'
} =
$format
|
$perm
;
return
$self
->taint;
}
sub
readlink
{
my
(
$self
) =
@_
;
unless
(
$self
->{
'dest'
} ) {
$self
->{
'dest'
} = CORE::
readlink
(
$self
->{
'path'
} ) or confess($!);
}
return
$self
->{
'dest'
};
}
sub
symlink
{
my
(
$self
,
$dest
) =
@_
;
unless
(
$self
->{
'sticky'
} ) {
symlink
(
$dest
,
$self
->{
'path'
} ) or confess($!);
}
$self
->{
'dest'
} =
$dest
;
return
$self
->taint;
}
1;