File Coverage

blib/lib/Filesys/POSIX.pm
Criterion Covered Total %
statement 190 190 100.0
branch 64 64 100.0
condition 6 6 100.0
subroutine 33 33 100.0
pod 20 20 100.0
total 313 313 100.0


line stmt bran cond sub pod time code
1             # Copyright (c) 2014, cPanel, Inc.
2             # All rights reserved.
3             # http://cpanel.net/
4             #
5             # This is free software; you can redistribute it and/or modify it under the same
6             # terms as Perl itself. See the LICENSE file for further details.
7              
8             package Filesys::POSIX;
9              
10 24     24   178640 use strict;
  24         38  
  24         739  
11 24     24   98 use warnings;
  24         27  
  24         504  
12              
13 24     24   8501 use Filesys::POSIX::Mem ();
  24         53  
  24         569  
14 24     24   8428 use Filesys::POSIX::FdTable ();
  24         43  
  24         557  
15 24     24   9721 use Filesys::POSIX::Path ();
  24         45  
  24         367  
16 24     24   8443 use Filesys::POSIX::VFS ();
  24         60  
  24         473  
17 24     24   129 use Filesys::POSIX::Bits;
  24         31  
  24         5826  
18              
19 24     24   9168 use Filesys::POSIX::IO ();
  24         43  
  24         415  
20 24     24   8759 use Filesys::POSIX::Mount ();
  24         34  
  24         391  
21 24     24   11037 use Filesys::POSIX::Userland ();
  24         38  
  24         474  
22              
23 24     24   106 use Filesys::POSIX::Error qw(throw);
  24         25  
  24         901  
24              
25 24     24   90 use Carp qw(confess);
  24         33  
  24         39567  
26              
27             our $VERSION = '0.9.16_0001';
28              
29             =head1 NAME
30              
31             Filesys::POSIX - Provide POSIX-like filesystem semantics in pure Perl
32              
33             =head1 SYNOPSIS
34              
35             use Filesys::POSIX
36             use Filesys::POSIX::Mem;
37              
38             my $fs = Filesys::POSIX->new(Filesys::POSIX::Mem->new,
39             'noatime' => 1
40             );
41              
42             $fs->umask(0700);
43             $fs->mkdir('foo');
44              
45             my $fd = $fs->open('/foo/bar', $O_CREAT | $O_WRONLY);
46             my $inode = $fs->fstat($fd);
47             $fs->printf("I have mode 0%o\n", $inode->{'mode'});
48             $fs->close($fd);
49              
50             =head1 DESCRIPTION
51              
52             Filesys::POSIX provides a fairly complete suite of tools comprising the
53             semantics of a POSIX filesystem, with path resolution, mount points, inodes,
54             a VFS, and some common utilities found in the userland. Some features not
55             found in a normal POSIX environment include the ability to perform cross-
56             mountpoint hard links (aliasing), mapping portions of the real filesystem into
57             an instance of a virtual filesystem, and allowing the developer to attach and
58             replace inodes at arbitrary points with replacements of their own
59             specification.
60              
61             Two filesystem types are provided out-of-the-box: A filesystem that lives in
62             memory completely, and a filesystem that provides a "portal" to any given
63             portion of the real underlying filesystem.
64              
65             By and large, the manner in which data is structured is quite similar to a
66             real kernel filesystem implementation, with some differences: VFS inodes are
67             not created for EVERY disk inode (only mount points); inodes are not referred
68             to numerically, but rather by Perl reference; and, directory entries can be
69             implemented in a device-specific manner, as long as they adhere to the normal
70             interface specified within.
71              
72             =head1 INSTANTIATING THE FILESYSTEM ENVIRONMENT
73              
74             =over
75              
76             =item Cnew($rootfs, %opts)>
77              
78             Create a new filesystem environment, specifying a reference to an
79             uninitialized instance of a filesystem type object to be mounted at the root
80             of the virtual filesystem. Options passed will be passed to the filesystem
81             initialization method C<$rootfs-Einit()> in flat hash form, and passed on
82             again to the VFS, where the options will be stored for later retrieval.
83              
84             =back
85              
86             =head1 ERROR HANDLING
87              
88             Errors are emitted in the form of exceptions thrown by
89             L|Carp/confess>, with full stack traces. Where possible,
90             L|perlvar/$!> is set with an appropriate error code from L, and a
91             stringified L|perlvar/$!> is thrown.
92              
93             =cut
94              
95             sub new {
96 42     42 1 220 my ( $class, $rootfs, %opts ) = @_;
97              
98 42 100       229 confess('No root filesystem specified') unless $rootfs;
99              
100 41         193 $rootfs->init(%opts);
101              
102 39         307 my $vfs = Filesys::POSIX::VFS->new->mount( $rootfs, '/', $rootfs->{'root'}, %opts );
103              
104 39         322 return bless {
105             'methods' => {},
106             'umask' => 022,
107             'fds' => Filesys::POSIX::FdTable->new,
108             'cwd' => $rootfs->{'root'},
109             'root' => $rootfs->{'root'},
110             'vfs' => $vfs,
111             'cwd' => $vfs->vnode( $rootfs->{'root'} ),
112             'root' => $vfs->vnode( $rootfs->{'root'} )
113             }, $class;
114             }
115              
116             =head1 SYSTEM CALLS
117              
118             =over
119              
120             =item C<$fs-Eumask()>
121              
122             =item C<$fs-Eumask($mode)>
123              
124             When called without an argument, the current umask value is returned. When a
125             value is specified, the current umask is modified to that value, and is
126             returned once set.
127              
128             =cut
129              
130             sub umask {
131 4     4 1 9 my ( $self, $umask ) = @_;
132              
133 4 100       15 return $self->{'umask'} = $umask if defined $umask;
134 2         12 return $self->{'umask'};
135             }
136              
137             sub _find_inode {
138 463     463   918 my ( $self, $path, %opts ) = @_;
139 463         1084 my $hier = Filesys::POSIX::Path->new($path);
140 463         712 my $dir = $self->{'cwd'};
141 463         352 my $inode;
142              
143 463 100       848 return $self->{'root'} if $hier->full eq '/';
144              
145 432         987 while ( $hier->count ) {
146 879         1450 my $item = $hier->shift;
147              
148             #
149             # We've encountered an absolute path. Start from the beginning.
150             #
151 879 100       1314 unless ($item) {
152 227         271 $dir = $self->{'root'};
153 227         438 next;
154             }
155              
156             #
157             # Before we go further, we need to resolve the current directory for
158             # a possible VFS inode in the event of a mountpoint or filesystem root.
159             #
160 652         1543 $dir = $self->{'vfs'}->vnode($dir);
161              
162 652 100       1241 unless ( $dir->{'dev'}->{'flags'}->{'noatime'} ) {
163 428         492 $dir->{'atime'} = time;
164             }
165              
166             #
167             # From this point, deal with the directory in terms of a directory entry.
168             #
169 652         1584 my $directory = $dir->directory;
170              
171 651 100       1205 if ( $item eq '.' ) {
    100          
172 97         117 $inode = $dir;
173             }
174             elsif ( $item eq '..' ) {
175 7         16 my $vnode = $self->{'vfs'}->vnode($dir);
176 7 100       19 $inode =
177             $vnode->{'parent'}
178             ? $vnode->{'parent'}
179             : $self->{'vfs'}->vnode( $directory->get('..') );
180             }
181             else {
182 547         1274 $inode = $self->{'vfs'}->vnode( $directory->get($item) );
183             }
184              
185 651         966 $! = 0;
186              
187 651 100       1048 throw &Errno::ENOENT unless $inode;
188              
189 633 100       1292 if ( $inode->link ) {
190 13 100 100     62 $hier = $hier->concat( $inode->readlink )
191             if $opts{'resolve_symlinks'} || $hier->count;
192             }
193             else {
194 620         1345 $dir = $inode;
195             }
196             }
197              
198 413         1346 return $inode;
199             }
200              
201             =item C<$fs-Estat($path)>
202              
203             Resolve the given path for an inode in the filesystem. If the inode found is
204             a symlink, the path of that symlink will be resolved in turn until the desired
205             inode is located.
206              
207             Paths will be resolved relative to the current working directory when not
208             prefixed with a slash ('C'), and will be resolved relative to the root
209             directory when prefixed with a slash ('C').
210              
211             =cut
212              
213             sub stat {
214 365     365 1 6127 my ( $self, $path ) = @_;
215 365         603 return $self->_find_inode( $path, 'resolve_symlinks' => 1 );
216             }
217              
218             =item C<$fs-Elstat($path)>
219              
220             Resolve the given path for an inode in the filesystem. Unlinke
221             C<$fs-Estat()>, the inode found will be returned literally in the case of a
222             symlink.
223              
224             =cut
225              
226             sub lstat {
227 98     98 1 213 my ( $self, $path ) = @_;
228 98         213 return $self->_find_inode($path);
229             }
230              
231             =item C<$fs-Efstat($fd)>
232              
233             Return the inode corresponding to the open file descriptor passed. An
234             exception will be thrown by the file descriptor lookup module if the file
235             descriptor passed does not correspond to an open file.
236              
237             =cut
238              
239             sub fstat {
240 51     51 1 701 my ( $self, $fd ) = @_;
241 51         134 return $self->{'fds'}->lookup($fd)->{'inode'};
242             }
243              
244             =item C<$fs-Echdir($path)>
245              
246             Change the current working directory to the path specified. An
247             C<$fs-Estat()> call will be used internally to lookup the inode for that
248             path; an ENOTDIR will be thrown unless the inode found is a directory. The
249             internal current working directory pointer will be updated with the directory
250             inode found; this same inode will also be returned.
251              
252             =cut
253              
254             sub chdir {
255 8     8 1 1961 my ( $self, $path ) = @_;
256 8         16 my $inode = $self->stat($path);
257              
258 8         13 $! = 0;
259              
260 8 100       17 throw &Errno::ENOTDIR unless $inode->dir;
261              
262 7         14 return $self->{'cwd'} = $inode;
263             }
264              
265             =item C<$fs-Efchdir($fd)>
266              
267             When passed a file descriptor for a directory, update the internal pointer to
268             the current working directory to that directory resolved from the file
269             descriptor table, and return the same directory inode. If the inode is not a
270             directory, an ENOTDIR will be thrown.
271              
272             =cut
273              
274             sub fchdir {
275 2     2 1 80 my ( $self, $fd ) = @_;
276 2         4 my $inode = $self->fstat($fd);
277              
278 2         3 $! = 0;
279              
280 2 100       7 throw &Errno::ENOTDIR unless $inode->dir;
281              
282 1         3 return $self->{'cwd'} = $inode;
283             }
284              
285             =item C<$fs-Echown($path, $uid, $gid)>
286              
287             Using C<$fs-Estat()> to locate the inode of the path specified, update that
288             inode object's 'uid' and 'gid' fields with the values specified. The inode of
289             the file modified will be returned.
290              
291             =cut
292              
293             sub chown {
294 1     1 1 5 my ( $self, $path, $uid, $gid ) = @_;
295 1         2 my $inode = $self->stat($path);
296              
297 1         4 $inode->chown( $uid, $gid );
298              
299 1         2 return $inode;
300             }
301              
302             =item C<$fs-Efchown($fd, $uid, $gid)>
303              
304             Using C<$fs-Efstat()> to locate the inode of the file descriptor specified,
305             update that inode object's 'uid' and 'gid' fields with the values specified. A
306             reference to the affected inode will be returned.
307              
308             =cut
309              
310             sub fchown {
311 1     1 1 3 my ( $self, $fd, $uid, $gid ) = @_;
312 1         2 my $inode = $self->fstat($fd);
313              
314 1         4 $inode->chown( $uid, $gid );
315              
316 1         2 return $inode;
317             }
318              
319             =item C<$fs-Echmod($path, $mode)>
320              
321             Using C<$fs-Estat()> to locate the inode of the path specified, update that
322             inode object's 'mode' field with the value specified. A reference to the
323             affected inode will be returned.
324              
325             =cut
326              
327             sub chmod {
328 10     10 1 16 my ( $self, $path, $mode ) = @_;
329 10         26 my $inode = $self->stat($path);
330              
331 10         23 $inode->chmod($mode);
332              
333 10         12 return $inode;
334             }
335              
336             =item C<$fs-Efchmod($fd, $mode)>
337              
338             Using C<$fs-Efstat()> to locate the inode of the file descriptor specified,
339             update that inode object's 'mode' field with the value specified. A reference
340             to that inode will be returned.
341              
342             =cut
343              
344             sub fchmod {
345 6     6 1 24 my ( $self, $fd, $mode ) = @_;
346 6         21 my $inode = $self->fstat($fd);
347              
348 6         17 $inode->chmod($mode);
349              
350 6         9 return $inode;
351             }
352              
353             =item C<$fs-Emkdir($path)>
354              
355             =item C<$fs-Emkdir($path, $mode)>
356              
357             Create a new directory at the path specified, applying the permissions field in
358             the mode value specified. If no mode is specified, the default permissions of
359             I<0777> will be modified by the current umask value. An ENOTDIR exception will
360             be thrown in case the intended parent of the directory to be created is not
361             actually a directory itself.
362              
363             A reference to the newly-created directory inode will be returned.
364              
365             =cut
366              
367             sub mkdir {
368 57     57 1 2689 my ( $self, $path, $mode ) = @_;
369 57         198 my $hier = Filesys::POSIX::Path->new($path);
370 57         155 my $name = $hier->basename;
371 57         137 my $parent = $self->stat( $hier->dirname );
372 57 100       139 my $perm = $mode ? $mode & ( $S_IPERM | $S_IPROT ) : $S_IPERM ^ $self->{'umask'};
373              
374 57         172 return $parent->child( $name, $perm | $S_IFDIR );
375             }
376              
377             =item C<$fs-Elink($src, $dest)>
378              
379             Using C<$fs-Estat()> to resolve the path of the link source, and the parent
380             of the link destination, C<$fs-Elink()> place a reference to the source
381             inode in the location specified by the destination.
382              
383             If a destination inode already exists, it will only be able to be replaced by
384             the source if both are either directories or non-directories. If the source
385             and destination are both directories, the destination will only be replaced if
386             the directory entry for the destination is empty.
387              
388             Links traversing filesystem mount points are not allowed. This functionality
389             is provided in the C call provided by the L
390             module. Upon success, a reference to the inode for which a new link is to be
391             created will be returned.
392              
393             Exceptions thrown:
394              
395             =over
396              
397             =item * EXDEV (Cross-device link)
398              
399             The inode resolved for the link source is not associated with the same device
400             as the inode of the destination's parent directory.
401              
402             =item * EISDIR (Is a directory)
403              
404             Thrown if the source inode is a directory. Hard links can only be made for
405             non-directory inodes.
406              
407             =item * EEXIST (File exists)
408              
409             Thrown if an entry at the destination path already exists.
410              
411             =back
412              
413             =cut
414              
415             sub link {
416 5     5 1 1929 my ( $self, $src, $dest ) = @_;
417 5         17 my $hier = Filesys::POSIX::Path->new($dest);
418 5         15 my $name = $hier->basename;
419 5         19 my $inode = $self->stat($src);
420 5         37 my $parent = $self->stat( $hier->dirname );
421 5         11 my $directory = $parent->directory;
422              
423 5         7 $! = 0;
424              
425 5 100       22 throw &Errno::EXDEV unless $inode->{'dev'} == $parent->{'dev'};
426 4 100       9 throw &Errno::EISDIR if $inode->dir;
427 3 100       10 throw &Errno::EEXIST if $directory->exists($name);
428              
429 2         7 $directory->set( $name, $inode );
430              
431 2         3 return $inode;
432             }
433              
434             =item C<$fs-Esymlink($old, $new)>
435              
436             The path in the first argument specified, C<$old>, is cleaned up using
437             Cfull>, and stored in a new symlink inode created
438             in the location specified by C<$new>. An EEXIST exception will be thrown if an
439             inode at the path indicated by C<$new> exists. A reference to the newly-created
440             symlink inode will be returned.
441              
442             =cut
443              
444             sub symlink {
445 14     14 1 6777 my ( $self, $old, $new ) = @_;
446 14         36 my $perms = $S_IPERM ^ $self->{'umask'};
447 14         62 my $hier = Filesys::POSIX::Path->new($new);
448 14         48 my $name = $hier->basename;
449 14         45 my $parent = $self->stat( $hier->dirname );
450              
451 14         58 return $parent->child( $name, $S_IFLNK | $perms )->symlink( Filesys::POSIX::Path->full($old) );
452             }
453              
454             =item C<$fs-Ereadlink($path)>
455              
456             Using C<$fs-Elstat()> to resolve the given path for an inode, the symlink
457             destination path associated with the inode is returned as a string. An EINVAL
458             exception is thrown unless the inode found is indeed a symlink.
459              
460             =cut
461              
462             sub readlink {
463 2     2 1 45 my ( $self, $path ) = @_;
464 2         5 my $inode = $self->lstat($path);
465              
466 2         4 $! = 0;
467              
468 2 100       4 throw &Errno::EINVAL unless $inode->link;
469              
470 1         3 return $inode->readlink;
471             }
472              
473             =item C<$fs-Eunlink($path)>
474              
475             Using C<$fs-Elstat()> to resolve the given path for an inode specified,
476             said inode will be removed from its parent directory entry. The following
477             exceptions will be thrown in the event of certain errors:
478              
479             =over
480              
481             =item * ENOENT (No such file or directory)
482              
483             No entry was found in the path's parent directory for the item specified in the
484             path.
485              
486             =item * EISDIR (Is a directory)
487              
488             C<$fs-Eunlink()> was called with a directory specified.
489             C<$fs-Ermdir()> must be used instead for removing directory inodes.
490              
491             =back
492              
493             Upon success, a reference to the inode removed from its parent directory will
494             be returned.
495              
496             =cut
497              
498             sub unlink {
499 4     4 1 116 my ( $self, $path ) = @_;
500 4         18 my $hier = Filesys::POSIX::Path->new($path);
501 4         11 my $name = $hier->basename;
502 4         12 my $parent = $self->lstat( $hier->dirname );
503 4         13 my $directory = $parent->directory;
504 4         15 my $inode = $directory->get($name);
505              
506 4         8 $! = 0;
507              
508 4 100       12 throw &Errno::ENOENT unless $inode;
509 3 100       10 throw &Errno::EISDIR if $inode->dir;
510              
511 2         10 $directory->delete($name);
512              
513 2         5 return $inode;
514             }
515              
516             =item C<$fs-Erename($old, $new)>
517              
518             Relocate the item specified by the C<$old> argument to the new path specified by
519             $new.
520              
521             Using C<$fs-Elstat>, the inode for the old pathname is resolved;
522             C<$fs-Estat> is then used to resolve the path of the parent directory of
523             the argument specified in C<$new>.
524              
525             If an inode exists at the path specified by C<$new>, it will be replaced by
526             C<$old> in the following circumstances:
527              
528             =over
529              
530             =item Both the source C<$old> and destination C<$new> are non-directory inodes.
531              
532             =item Both the source C<$old> and destination C<$new> are directory inodes, and
533             the destination is empty.
534              
535             =back
536              
537             The following exceptions are thrown for error conditions:
538              
539             =over
540              
541             =item * EPERM (Operation not permitted)
542              
543             Currently, C<$fs-Erename()> cannot operate if the inode at the old location
544             is an inode associated with a Filesys::POSIX::Real filesystem type.
545              
546             =item * EXDEV (Cross-device link)
547              
548             The inode at the old path does not exist on the same filesystem device as the
549             inode of the parent directory specified in the new path.
550              
551             =item * ENOTDIR (Not a directory)
552              
553             The old inode is a directory, but an existing inode found in the new path
554             specified, is not.
555              
556             =item * EISDIR (Is a directory)
557              
558             The old inode is not a directory, but an existing inode found in the new path
559             specified, is.
560              
561             =item * ENOTEMPTY (Directory not empty)
562              
563             Both the old and new paths correspond to a directory, but the new path is not
564             of an empty directory.
565              
566             =back
567              
568             Upon success, a reference to the inode to be renamed will be returned.
569              
570             =cut
571              
572             sub rename {
573 12     12 1 10870 my ( $self, $old, $new ) = @_;
574              
575 12         37 my $inode = $self->lstat($old);
576              
577 12         34 my $old_hier = Filesys::POSIX::Path->new($old);
578 12         33 my $old_name = $old_hier->basename;
579 12         29 my $old_parent = $self->stat( $old_hier->dirname );
580 12         30 my $old_dir = $old_parent->directory;
581              
582 12         35 my $new_hier = Filesys::POSIX::Path->new($new);
583 12         32 my $new_name = $new_hier->basename;
584 12         29 my $new_parent = $self->stat( $new_hier->dirname );
585 12         30 my $new_dir = $new_parent->directory;
586              
587 12         20 $! = 0;
588              
589 12 100       44 throw &Errno::EXDEV unless $inode->{'dev'} eq $new_parent->{'dev'};
590              
591 11 100       27 if ( my $existing = $new_dir->get($new_name) ) {
592 5 100       12 if ( $inode->dir ) {
593 3 100       7 throw &Errno::ENOTDIR unless $existing->dir;
594 2 100       6 throw &Errno::ENOTEMPTY unless $existing->empty;
595             }
596             else {
597 2 100       4 throw &Errno::EISDIR if $existing->dir;
598             }
599             }
600              
601 8         74 $new_dir->rename_member( $inode, $old_dir, $old_name, $new_name );
602              
603 7         44 return $inode;
604             }
605              
606             =item C<$fs-Ermdir($path)>
607              
608             Unlinks the directory inode at the specified path. Exceptions are thrown in
609             the following conditions:
610              
611             =over
612              
613             =item * ENOENT (No such file or directory)
614              
615             No inode exists by the name specified in the final component of the path in
616             the parent directory specified in the path.
617              
618             =item * EBUSY (Device or resource busy)
619              
620             The directory specified is an active mount point.
621              
622             =item * ENOTDIR (Not a directory)
623              
624             The inode found at C<$path> is not a directory.
625              
626             =item * ENOTEMPTY (Directory not empty)
627              
628             The directory is not empty.
629              
630             =back
631              
632             Upon success, a reference to the inode of the directory to be removed will be
633             returned.
634              
635             =cut
636              
637             sub rmdir {
638 5     5 1 166 my ( $self, $path ) = @_;
639 5         16 my $hier = Filesys::POSIX::Path->new($path);
640 5         13 my $name = $hier->basename;
641 5         12 my $parent = $self->lstat( $hier->dirname );
642 5         13 my $directory = $parent->directory;
643 5         13 my $inode = $directory->get($name);
644              
645 5         8 $! = 0;
646              
647 5 100       15 throw &Errno::ENOENT unless $inode;
648              
649 4 100       9 throw &Errno::EBUSY if $self->{'vfs'}->statfs(
650             $self->stat($path),
651             'exact' => 1, 'silent' => 1
652             );
653              
654 3 100       13 throw &Errno::ENOTEMPTY unless $inode->empty;
655              
656 1         4 $directory->delete($name);
657              
658 1         2 return $inode;
659             }
660              
661             =item C<$fs-Emknod($path, $mode)>
662              
663             =item C<$fs-Emknod($path, $mode, $dev)>
664              
665             Create a new inode at the specified C<$path>, with the inode permissions and
666             format specified in the C<$mode> argument. If C<$mode> specifies a C<$S_IFCHR>
667             or C<$S_IFBLK> value, then the device number specified in C<$dev> will be given
668             to the new inode.
669              
670             Code contained within the C distribution assumes that the device
671             identifier shall contain the major and minor numbers in separate 16-bit fields,
672             in the following manner:
673              
674             my $major = ($dev & 0xffff0000) >> 16;
675             my $minor = $dev & 0x0000ffff;
676              
677             Returns a reference to a L object upon success.
678              
679             =cut
680              
681             sub mknod {
682 30     30 1 3673 my ( $self, $path, $mode, $dev ) = @_;
683 30         75 my $hier = Filesys::POSIX::Path->new($path);
684 30         67 my $name = $hier->basename;
685 30         63 my $parent = $self->lstat( $hier->dirname );
686 29         58 my $directory = $parent->directory;
687              
688 29         32 my $format = $mode & $S_IFMT;
689 29         32 my $perms = $mode & $S_IPERM;
690              
691 29         41 $! = 0;
692              
693 29 100       55 throw &Errno::EINVAL unless $format;
694 28 100       57 throw &Errno::EEXIST if $directory->exists($name);
695              
696 27         73 my $inode = $parent->child( $name, $format | $perms );
697              
698 27 100 100     84 if ( $format == $S_IFCHR || $format == $S_IFBLK ) {
699 24         31 $inode->{'rdev'} = $dev;
700             }
701              
702 27         82 return $inode;
703             }
704              
705             =item C<$fs-Emkfifo($path, $mode)>
706              
707             Create a new FIFO device at the specified C<$path>, with the permissions listed
708             in C<$mode>. Internally, this function is a frontend to
709             Cmknod>.
710              
711             Returns a reference to a L object upon success.
712              
713             =cut
714              
715             sub mkfifo {
716 2     2 1 1011 my ( $self, $path, $mode ) = @_;
717              
718 2         5 my $format = $S_IFIFO;
719 2         4 my $perms = $mode & $S_IPERM;
720              
721 2         7 return $self->mknod( $path, $format | $perms );
722             }
723              
724             =back
725              
726             =cut
727              
728             1;
729              
730             __END__