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 25     25   118928 use strict;
  25         28  
  25         574  
11 25     25   76 use warnings;
  25         38  
  25         463  
12              
13 25     25   8077 use Filesys::POSIX::Mem ();
  25         46  
  25         385  
14 25     25   8301 use Filesys::POSIX::FdTable ();
  25         40  
  25         346  
15 25     25   8185 use Filesys::POSIX::Path ();
  25         35  
  25         337  
16 25     25   8247 use Filesys::POSIX::VFS ();
  25         44  
  25         379  
17 25     25   101 use Filesys::POSIX::Bits;
  25         26  
  25         5254  
18              
19 25     25   8530 use Filesys::POSIX::IO ();
  25         34  
  25         386  
20 25     25   8479 use Filesys::POSIX::Mount ();
  25         36  
  25         397  
21 25     25   8539 use Filesys::POSIX::Userland ();
  25         36  
  25         441  
22              
23 25     25   108 use Filesys::POSIX::Error qw(throw);
  25         22  
  25         851  
24              
25 25     25   83 use Carp qw(confess);
  25         24  
  25         35566  
26              
27             our $VERSION = '0.9.18_0002';
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 45     45 1 189 my ( $class, $rootfs, %opts ) = @_;
97              
98 45 100       205 confess('No root filesystem specified') unless $rootfs;
99              
100 44         169 $rootfs->init(%opts);
101              
102 40         281 my $vfs = Filesys::POSIX::VFS->new->mount( $rootfs, '/', $rootfs->{'root'}, %opts );
103              
104             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 40         336 '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 8 my ( $self, $umask ) = @_;
132              
133 4 100       14 return $self->{'umask'} = $umask if defined $umask;
134 2         7 return $self->{'umask'};
135             }
136              
137             sub _find_inode {
138 483     483   699 my ( $self, $path, %opts ) = @_;
139 483         906 my $hier = Filesys::POSIX::Path->new($path);
140 483         574 my $dir = $self->{'cwd'};
141 483         342 my $inode;
142              
143 483 100       790 return $self->{'root'} if $hier->full eq '/';
144              
145 452         888 while ( $hier->count ) {
146 929         1313 my $item = $hier->shift;
147              
148             #
149             # We've encountered an absolute path. Start from the beginning.
150             #
151 929 100       1273 unless (length $item) {
152 232         234 $dir = $self->{'root'};
153 232         381 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 697         1269 $dir = $self->{'vfs'}->vnode($dir);
161              
162 697 100       1104 unless ( $dir->{'dev'}->{'flags'}->{'noatime'} ) {
163 463         477 $dir->{'atime'} = time;
164             }
165              
166             #
167             # From this point, deal with the directory in terms of a directory entry.
168             #
169 697         1348 my $directory = $dir->directory;
170              
171 696 100       1127 if ( $item eq '.' ) {
    100          
172 103         89 $inode = $dir;
173             }
174             elsif ( $item eq '..' ) {
175 7         15 my $vnode = $self->{'vfs'}->vnode($dir);
176             $inode =
177             $vnode->{'parent'}
178             ? $vnode->{'parent'}
179 7 100       18 : $self->{'vfs'}->vnode( $directory->get('..') );
180             }
181             else {
182 586         1109 $inode = $self->{'vfs'}->vnode( $directory->get($item) );
183             }
184              
185 696         870 $! = 0;
186              
187 696 100       898 throw &Errno::ENOENT unless $inode;
188              
189 675 100       1144 if ( $inode->link ) {
190             $hier = $hier->concat( $inode->readlink )
191 16 100 100     73 if $opts{'resolve_symlinks'} || $hier->count;
192             }
193             else {
194 659         1197 $dir = $inode;
195             }
196             }
197              
198 430         1095 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 383     383 1 8131 my ( $self, $path ) = @_;
215 383         600 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 100     100 1 169 my ( $self, $path ) = @_;
228 100         171 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 604 my ( $self, $fd ) = @_;
241 51         141 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 1816 my ( $self, $path ) = @_;
256 8         16 my $inode = $self->stat($path);
257              
258 8         10 $! = 0;
259              
260 8 100       16 throw &Errno::ENOTDIR unless $inode->dir;
261              
262 7         13 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 64 my ( $self, $fd ) = @_;
276 2         3 my $inode = $self->fstat($fd);
277              
278 2         4 $! = 0;
279              
280 2 100       3 throw &Errno::ENOTDIR unless $inode->dir;
281              
282 1         2 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         5 my $inode = $self->stat($path);
296              
297 1         4 $inode->chown( $uid, $gid );
298              
299 1         1 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 2 my ( $self, $fd, $uid, $gid ) = @_;
312 1         2 my $inode = $self->fstat($fd);
313              
314 1         11 $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 10 my ( $self, $path, $mode ) = @_;
329 10         15 my $inode = $self->stat($path);
330              
331 10         17 $inode->chmod($mode);
332              
333 10         10 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         14 my $inode = $self->fstat($fd);
347              
348 6         18 $inode->chmod($mode);
349              
350 6         6 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 59     59 1 2283 my ( $self, $path, $mode ) = @_;
369 59         160 my $hier = Filesys::POSIX::Path->new($path);
370 59         138 my $name = $hier->basename;
371 59         126 my $parent = $self->stat( $hier->dirname );
372 59 100       121 my $perm = $mode ? $mode & ( $S_IPERM | $S_IPROT ) : $S_IPERM ^ $self->{'umask'};
373              
374 59         157 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 1512 my ( $self, $src, $dest ) = @_;
417 5         16 my $hier = Filesys::POSIX::Path->new($dest);
418 5         11 my $name = $hier->basename;
419 5         7 my $inode = $self->stat($src);
420 5         11 my $parent = $self->stat( $hier->dirname );
421 5         10 my $directory = $parent->directory;
422              
423 5         6 $! = 0;
424              
425 5 100       19 throw &Errno::EXDEV unless $inode->{'dev'} == $parent->{'dev'};
426 4 100       8 throw &Errno::EISDIR if $inode->dir;
427 3 100       7 throw &Errno::EEXIST if $directory->exists($name);
428              
429 2         5 $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 15     15 1 5527 my ( $self, $old, $new ) = @_;
446 15         38 my $perms = $S_IPERM ^ $self->{'umask'};
447 15         45 my $hier = Filesys::POSIX::Path->new($new);
448 15         38 my $name = $hier->basename;
449 15         37 my $parent = $self->stat( $hier->dirname );
450              
451 15         54 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 37 my ( $self, $path ) = @_;
464 2         4 my $inode = $self->lstat($path);
465              
466 2         4 $! = 0;
467              
468 2 100       3 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 94 my ( $self, $path ) = @_;
500 4         13 my $hier = Filesys::POSIX::Path->new($path);
501 4         8 my $name = $hier->basename;
502 4         9 my $parent = $self->lstat( $hier->dirname );
503 4         7 my $directory = $parent->directory;
504 4         10 my $inode = $directory->get($name);
505              
506 4         5 $! = 0;
507              
508 4 100       10 throw &Errno::ENOENT unless $inode;
509 3 100       5 throw &Errno::EISDIR if $inode->dir;
510              
511 2         5 $directory->delete($name);
512              
513 2         3 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 11     11 1 1624 my ( $self, $old, $new ) = @_;
574              
575 11         23 my $inode = $self->lstat($old);
576              
577 11         24 my $old_hier = Filesys::POSIX::Path->new($old);
578 11         46 my $old_name = $old_hier->basename;
579 11         26 my $old_parent = $self->stat( $old_hier->dirname );
580 11         24 my $old_dir = $old_parent->directory;
581              
582 11         26 my $new_hier = Filesys::POSIX::Path->new($new);
583 11         24 my $new_name = $new_hier->basename;
584 11         46 my $new_parent = $self->stat( $new_hier->dirname );
585 11         24 my $new_dir = $new_parent->directory;
586              
587 11         14 $! = 0;
588              
589 11 100       40 throw &Errno::EXDEV unless $inode->{'dev'} eq $new_parent->{'dev'};
590              
591 10 100       23 if ( my $existing = $new_dir->get($new_name) ) {
592 5 100       9 if ( $inode->dir ) {
593 3 100       6 throw &Errno::ENOTDIR unless $existing->dir;
594 2 100       5 throw &Errno::ENOTEMPTY unless $existing->empty;
595             }
596             else {
597 2 100       9 throw &Errno::EISDIR if $existing->dir;
598             }
599             }
600              
601 7         30 $new_dir->rename_member( $inode, $old_dir, $old_name, $new_name );
602              
603 7         34 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 135 my ( $self, $path ) = @_;
639 5         14 my $hier = Filesys::POSIX::Path->new($path);
640 5         10 my $name = $hier->basename;
641 5         8 my $parent = $self->lstat( $hier->dirname );
642 5         10 my $directory = $parent->directory;
643 5         10 my $inode = $directory->get($name);
644              
645 5         7 $! = 0;
646              
647 5 100       12 throw &Errno::ENOENT unless $inode;
648              
649 4 100       6 throw &Errno::EBUSY if $self->{'vfs'}->statfs(
650             $self->stat($path),
651             'exact' => 1, 'silent' => 1
652             );
653              
654 3 100       9 throw &Errno::ENOTEMPTY unless $inode->empty;
655              
656 1         2 $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 3497 my ( $self, $path, $mode, $dev ) = @_;
683 30         59 my $hier = Filesys::POSIX::Path->new($path);
684 30         61 my $name = $hier->basename;
685 30         55 my $parent = $self->lstat( $hier->dirname );
686 29         53 my $directory = $parent->directory;
687              
688 29         31 my $format = $mode & $S_IFMT;
689 29         26 my $perms = $mode & $S_IPERM;
690              
691 29         26 $! = 0;
692              
693 29 100       41 throw &Errno::EINVAL unless $format;
694 28 100       47 throw &Errno::EEXIST if $directory->exists($name);
695              
696 27         78 my $inode = $parent->child( $name, $format | $perms );
697              
698 27 100 100     77 if ( $format == $S_IFCHR || $format == $S_IFBLK ) {
699 24         21 $inode->{'rdev'} = $dev;
700             }
701              
702 27         68 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 910 my ( $self, $path, $mode ) = @_;
717              
718 2         4 my $format = $S_IFIFO;
719 2         4 my $perms = $mode & $S_IPERM;
720              
721 2         6 return $self->mknod( $path, $format | $perms );
722             }
723              
724             =back
725              
726             =cut
727              
728             1;
729              
730             __END__