2 % (c) The AQUA Project, Glasgow University, 1994-1997
4 \section[Directory]{Directory interface}
6 A directory contains a series of entries, each of which is a named
7 reference to a file system object (file, directory etc.). Some
8 entries may be hidden, inaccessible, or have some administrative
9 function (e.g. "." or ".." under POSIX), but in this standard all such
10 entries are considered to form part of the directory contents.
11 Entries in sub-directories are not, however, considered to form part
12 of the directory contents.
14 Each file system object is referenced by a {\em path}. There is
15 normally at least one absolute path to each file system object. In
16 some operating systems, it may also be possible to have paths which
17 are relative to the current directory.
20 {-# OPTIONS -#include <sys/stat.h> -#include <dirent.h> -#include "cbits/stgio.h" #-}
23 Permissions(Permissions),
52 import PrelPack ( unpackNBytesST )
54 import Time ( ClockTime(..) )
59 %*********************************************************
61 \subsection{Signatures}
63 %*********************************************************
66 createDirectory :: FilePath -> IO ()
67 removeDirectory :: FilePath -> IO ()
68 removeFile :: FilePath -> IO ()
69 renameDirectory :: FilePath -> FilePath -> IO ()
70 renameFile :: FilePath -> FilePath -> IO ()
71 getDirectoryContents :: FilePath -> IO [FilePath]
72 getCurrentDirectory :: IO FilePath
73 setCurrentDirectory :: FilePath -> IO ()
74 doesFileExist :: FilePath -> IO Bool
75 doesDirectoryExist :: FilePath -> IO Bool
76 getPermissions :: FilePath -> IO Permissions
77 setPermissions :: FilePath -> Permissions -> IO ()
79 getModificationTime :: FilePath -> IO ClockTime
85 foreign import stdcall "libHS_cbits.so" "createDirectory" primCreateDirectory :: CString -> IO Int
86 foreign import stdcall "libHS_cbits.so" "removeDirectory" primRemoveDirectory :: CString -> IO Int
87 foreign import stdcall "libHS_cbits.so" "removeFile" primRemoveFile :: CString -> IO Int
88 foreign import stdcall "libHS_cbits.so" "renameDirectory" primRenameDirectory :: CString -> CString -> IO Int
89 foreign import stdcall "libHS_cbits.so" "renameFile" primRenameFile :: CString -> CString -> IO Int
90 foreign import stdcall "libHS_cbits.so" "openDir__" primOpenDir :: CString -> IO Addr
91 foreign import stdcall "libHS_cbits.so" "readDir__" primReadDir :: Addr -> IO Addr
92 foreign import stdcall "libHS_cbits.so" "get_dirent_d_name" primGetDirentDName :: Addr -> IO Addr
93 foreign import stdcall "libHS_cbits.so" "setCurrentDirectory" primSetCurrentDirectory :: CString -> IO Int
94 foreign import stdcall "libHS_cbits.so" "getCurrentDirectory" primGetCurrentDirectory :: IO Addr
95 foreign import stdcall "libc.so.6" "free" primFree :: Addr -> IO ()
96 foreign import stdcall "libc.so.6" "malloc" primMalloc :: Word -> IO Addr
97 foreign import stdcall "libc.so.6" "chmod" primChmod :: CString -> Word -> IO Int
101 %*********************************************************
103 \subsection{Permissions}
105 %*********************************************************
107 The @Permissions@ type is used to record whether certain
108 operations are permissible on a file/directory:
109 [to whom? - owner/group/world - the Report don't say much]
115 executable, searchable :: Bool
116 } deriving (Eq, Ord, Read, Show)
119 %*********************************************************
121 \subsection{Implementation}
123 %*********************************************************
125 @createDirectory dir@ creates a new directory {\em dir} which is
126 initially empty, or as near to empty as the operating system
129 The operation may fail with:
132 \item @isPermissionError@ / @PermissionDenied@
133 The process has insufficient privileges to perform the operation.
135 \item @isAlreadyExistsError@ / @AlreadyExists@
136 The operand refers to a directory that already exists.
138 \item @HardwareFault@
139 A physical I/O error has occurred.
141 \item @InvalidArgument@
142 The operand is not a valid directory name.
143 @[ENAMETOOLONG, ELOOP]@
145 There is no path to the directory.
147 \item @ResourceExhausted@
148 Insufficient resources (virtual memory, process file descriptors,
149 physical disk space, etc.) are available to perform the operation.
150 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
151 \item @InappropriateType@
152 The path refers to an existing non-directory object.
158 createDirectory path = do
160 rc <- primCreateDirectory (primPackString path)
162 rc <- _ccall_ createDirectory path
164 if rc == 0 then return () else
165 constructErrorAndFailWithInfo "createDirectory" path
168 @removeDirectory dir@ removes an existing directory {\em dir}. The
169 implementation may specify additional constraints which must be
170 satisfied before a directory can be removed (e.g. the directory has to
171 be empty, or may not be in use by other processes). It is not legal
172 for an implementation to partially remove a directory unless the
173 entire directory is removed. A conformant implementation need not
174 support directory removal in all situations (e.g. removal of the root
177 The operation may fail with:
179 \item @HardwareFault@
180 A physical I/O error has occurred.
182 \item @InvalidArgument@
183 The operand is not a valid directory name.
184 @[ENAMETOOLONG, ELOOP]@
185 \item @isDoesNotExist@ / @NoSuchThing@
186 The directory does not exist.
188 \item @isPermissionError@ / @PermissionDenied@
189 The process has insufficient privileges to perform the operation.
190 @[EROFS, EACCES, EPERM]@
191 \item @UnsatisfiedConstraints@
192 Implementation-dependent constraints are not satisfied.
193 @[EBUSY, ENOTEMPTY, EEXIST]@
194 \item @UnsupportedOperation@
195 The implementation does not support removal in this situation.
197 \item @InappropriateType@
198 The operand refers to an existing non-directory object.
203 removeDirectory path = do
205 rc <- primRemoveDirectory (primPackString path)
207 rc <- _ccall_ removeDirectory path
212 constructErrorAndFailWithInfo "removeDirectory" path
215 @removeFile file@ removes the directory entry for an existing file
216 {\em file}, where {\em file} is not itself a directory. The
217 implementation may specify additional constraints which must be
218 satisfied before a file can be removed (e.g. the file may not be in
219 use by other processes).
221 The operation may fail with:
223 \item @HardwareFault@
224 A physical I/O error has occurred.
226 \item @InvalidArgument@
227 The operand is not a valid file name.
228 @[ENAMETOOLONG, ELOOP]@
229 \item @isDoesNotExist@ / @NoSuchThing@
230 The file does not exist.
232 \item @isPermissionError@ / @PermissionDenied@
233 The process has insufficient privileges to perform the operation.
234 @[EROFS, EACCES, EPERM]@
235 \item @UnsatisfiedConstraints@
236 Implementation-dependent constraints are not satisfied.
238 \item @InappropriateType@
239 The operand refers to an existing directory.
246 rc <- primRemoveFile (primPackString path)
248 rc <- _ccall_ removeFile path
253 constructErrorAndFailWithInfo "removeFile" path
256 @renameDirectory old@ {\em new} changes the name of an existing
257 directory from {\em old} to {\em new}. If the {\em new} directory
258 already exists, it is atomically replaced by the {\em old} directory.
259 If the {\em new} directory is neither the {\em old} directory nor an
260 alias of the {\em old} directory, it is removed as if by
261 $removeDirectory$. A conformant implementation need not support
262 renaming directories in all situations (e.g. renaming to an existing
263 directory, or across different physical devices), but the constraints
266 The operation may fail with:
268 \item @HardwareFault@
269 A physical I/O error has occurred.
271 \item @InvalidArgument@
272 Either operand is not a valid directory name.
273 @[ENAMETOOLONG, ELOOP]@
274 \item @isDoesNotExistError@ / @NoSuchThing@
275 The original directory does not exist, or there is no path to the target.
277 \item @isPermissionError@ / @PermissionDenied@
278 The process has insufficient privileges to perform the operation.
279 @[EROFS, EACCES, EPERM]@
280 \item @ResourceExhausted@
281 Insufficient resources are available to perform the operation.
282 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
283 \item @UnsatisfiedConstraints@
284 Implementation-dependent constraints are not satisfied.
285 @[EBUSY, ENOTEMPTY, EEXIST]@
286 \item @UnsupportedOperation@
287 The implementation does not support renaming in this situation.
289 \item @InappropriateType@
290 Either path refers to an existing non-directory object.
295 renameDirectory opath npath = do
297 rc <- primRenameDirectory (primPackString opath) (primPackString npath)
299 rc <- _ccall_ renameDirectory opath npath
304 constructErrorAndFailWithInfo "renameDirectory" ("old: " ++ opath ++ ",new: " ++ npath)
307 @renameFile old@ {\em new} changes the name of an existing file system
308 object from {\em old} to {\em new}. If the {\em new} object already
309 exists, it is atomically replaced by the {\em old} object. Neither
310 path may refer to an existing directory. A conformant implementation
311 need not support renaming files in all situations (e.g. renaming
312 across different physical devices), but the constraints must be
315 The operation may fail with:
317 \item @HardwareFault@
318 A physical I/O error has occurred.
320 \item @InvalidArgument@
321 Either operand is not a valid file name.
322 @[ENAMETOOLONG, ELOOP]@
323 \item @isDoesNotExistError@ / @NoSuchThing@
324 The original file does not exist, or there is no path to the target.
326 \item @isPermissionError@ / @PermissionDenied@
327 The process has insufficient privileges to perform the operation.
328 @[EROFS, EACCES, EPERM]@
329 \item @ResourceExhausted@
330 Insufficient resources are available to perform the operation.
331 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
332 \item @UnsatisfiedConstraints@
333 Implementation-dependent constraints are not satisfied.
335 \item @UnsupportedOperation@
336 The implementation does not support renaming in this situation.
338 \item @InappropriateType@
339 Either path refers to an existing directory.
340 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
344 renameFile opath npath = do
346 rc <- primRenameFile (primPackString opath) (primPackString npath)
348 rc <- _ccall_ renameFile opath npath
353 constructErrorAndFailWithInfo "renameFile" opath
356 @getDirectoryContents dir@ returns a list of {\em all} entries
359 The operation may fail with:
361 \item @HardwareFault@
362 A physical I/O error has occurred.
364 \item @InvalidArgument@
365 The operand is not a valid directory name.
366 @[ENAMETOOLONG, ELOOP]@
367 \item @isDoesNotExistError@ / @NoSuchThing@
368 The directory does not exist.
370 \item @isPermissionError@ / @PermissionDenied@
371 The process has insufficient privileges to perform the operation.
373 \item @ResourceExhausted@
374 Insufficient resources are available to perform the operation.
376 \item @InappropriateType@
377 The path refers to an existing non-directory object.
382 --getDirectoryContents :: FilePath -> IO [FilePath]
384 getDirectoryContents path = do
385 dir <- primOpenDir (primPackString path)
387 then constructErrorAndFailWithInfo "getDirectoryContents" path
390 loop :: Addr -> IO [String]
392 dirent_ptr <- primReadDir dir
393 if dirent_ptr == nullAddr
395 -- readDir__ implicitly performs closedir() when the
399 str <- primGetDirentDName dirent_ptr
400 entry <- primUnpackCString str
402 return (entry:entries)
404 getDirectoryContents path = do
405 dir <- _ccall_ openDir__ path
407 then constructErrorAndFailWithInfo "getDirectoryContents" path
410 loop :: Addr -> IO [String]
412 dirent_ptr <- _ccall_ readDir__ dir
413 if (dirent_ptr::Addr) == ``NULL''
415 -- readDir__ implicitly performs closedir() when the
419 str <- _casm_ `` %r=(char*)((struct dirent*)%0)->d_name; '' dirent_ptr
420 -- not using the unpackCString function here, since we have to force
421 -- the unmarshalling of the directory entry right here as subsequent
422 -- calls to readdir() may overwrite it.
423 len <- _ccall_ strlen str
424 entry <- stToIO (unpackNBytesST str len)
426 return (entry:entries)
430 If the operating system has a notion of current directories,
431 @getCurrentDirectory@ returns an absolute path to the
432 current directory of the calling process.
434 The operation may fail with:
436 \item @HardwareFault@
437 A physical I/O error has occurred.
439 \item @isDoesNotExistError@ / @NoSuchThing@
440 There is no path referring to the current directory.
441 @[EPERM, ENOENT, ESTALE...]@
442 \item @isPermissionError@ / @PermissionDenied@
443 The process has insufficient privileges to perform the operation.
445 \item @ResourceExhausted@
446 Insufficient resources are available to perform the operation.
447 \item @UnsupportedOperation@
448 The operating system has no notion of current directory.
452 getCurrentDirectory = do
454 str <- primGetCurrentDirectory
456 str <- _ccall_ getCurrentDirectory
461 pwd <- primUnpackCString str
464 -- don't use unpackCString (see getDirectoryContents above)
465 len <- _ccall_ strlen str
466 pwd <- stToIO (unpackNBytesST str len)
471 constructErrorAndFail "getCurrentDirectory"
474 If the operating system has a notion of current directories,
475 @setCurrentDirectory dir@ changes the current
476 directory of the calling process to {\em dir}.
478 The operation may fail with:
480 \item @HardwareFault@
481 A physical I/O error has occurred.
483 \item @InvalidArgument@
484 The operand is not a valid directory name.
485 @[ENAMETOOLONG, ELOOP]@
486 \item @isDoesNotExistError@ / @NoSuchThing@
487 The directory does not exist.
489 \item @isPermissionError@ / @PermissionDenied@
490 The process has insufficient privileges to perform the operation.
492 \item @UnsupportedOperation@
493 The operating system has no notion of current directory, or the
494 current directory cannot be dynamically changed.
495 \item @InappropriateType@
496 The path refers to an existing non-directory object.
501 setCurrentDirectory path = do
503 rc <- primSetCurrentDirectory (primPackString path)
505 rc <- _ccall_ setCurrentDirectory path
509 else constructErrorAndFailWithInfo "setCurrentDirectory" path
514 --doesFileExist :: FilePath -> IO Bool
516 foreign import stdcall "libc.so.6" "access" primAccess :: PrimByteArray -> Int -> IO Int
517 foreign import stdcall "libHS_cbits.so" "const_F_OK" const_F_OK :: Int
519 doesFileExist name = do
520 rc <- primAccess (primPackString name) const_F_OK
523 doesFileExist name = do
524 rc <- _ccall_ access name (``F_OK''::Int)
528 --doesDirectoryExist :: FilePath -> IO Bool
529 doesDirectoryExist name =
530 (getFileStatus name >>= \ st -> return (isDirectory st))
532 (\ _ -> return False)
535 --getModificationTime :: FilePath -> IO ClockTime
536 getModificationTime name =
537 getFileStatus name >>= \ st ->
541 --getPermissions :: FilePath -> IO Permissions
542 getPermissions name =
543 getFileStatus name >>= \ st ->
546 isect v = intersectFileMode v fm == v
550 readable = isect ownerReadMode,
551 writeable = isect ownerWriteMode,
552 executable = not (isDirectory st) && isect ownerExecuteMode,
553 searchable = not (isRegularFile st) && isect ownerExecuteMode
557 --setPermissions :: FilePath -> Permissions -> IO ()
559 setPermissions name (Permissions r w e s) = do
561 read = if r then ownerReadMode else emptyFileMode
562 write = if w then ownerWriteMode else emptyFileMode
563 exec = if e || s then ownerExecuteMode else emptyFileMode
565 mode = read `unionFileMode` (write `unionFileMode` exec)
567 rc <- primChmod (primPackString name) mode
570 else fail (IOError Nothing SystemError "setPermissions" "insufficient permissions")
572 setPermissions name (Permissions r w e s) = do
574 read# = case (if r then ownerReadMode else ``0'') of { W# x# -> x# }
575 write# = case (if w then ownerWriteMode else ``0'') of { W# x# -> x# }
576 exec# = case (if e || s then ownerExecuteMode else ``0'') of { W# x# -> x# }
578 mode = I# (word2Int# (read# `or#` write# `or#` exec#))
580 rc <- _ccall_ chmod name mode
583 else fail (IOError Nothing SystemError "setPermissions" "insufficient permissions")
588 (Sigh)..copied from Posix.Files to avoid dep. on posix library
592 foreign import stdcall "libHS_cbits.so" "sizeof_stat" sizeof_stat :: Int
593 foreign import stdcall "libHS_cbits.so" "prim_stat" primStat :: PrimByteArray -> PrimMutableByteArray RealWorld -> IO Int
595 type FileStatus = PrimByteArray
597 getFileStatus :: FilePath -> IO FileStatus
598 getFileStatus name = do
599 bytes <- primNewByteArray sizeof_stat
600 rc <- primStat (primPackString name) bytes
602 then primUnsafeFreezeByteArray bytes
603 else fail (IOError Nothing SystemError "getFileStatus" "")
605 type FileStatus = ByteArray Int
607 getFileStatus :: FilePath -> IO FileStatus
608 getFileStatus name = do
609 bytes <- stToIO (newCharArray (0,``sizeof(struct stat)''))
610 rc <- _casm_ ``%r = stat(%0,(struct stat *)%1);'' name bytes
612 then stToIO (unsafeFreezeByteArray bytes)
613 else fail (IOError Nothing SystemError "getFileStatus" "")
615 modificationTime :: FileStatus -> IO ClockTime
616 modificationTime stat = do
618 _casm_ ``((unsigned long *)%1)[0] = ((struct stat *)%0)->st_mtime;'' stat i1
619 secs <- cvtUnsigned i1
622 malloc1 = IO $ \ s# ->
623 case newIntArray# 1# s# of
624 (# s2#, barr# #) -> (# s2#, MutableByteArray bnds barr# #)
627 -- The C routine fills in an unsigned word. We don't have `unsigned2Integer#,'
628 -- so we freeze the data bits and use them for an MP_INT structure. Note that
629 -- zero is still handled specially, although (J# 1# 1# (ptr to 0#)) is probably
630 -- acceptable to gmp.
632 cvtUnsigned (MutableByteArray _ arr#) = IO $ \ s# ->
633 case readIntArray# arr# 0# s# of
638 case unsafeFreezeByteArray# arr# s2# of
639 (# s3#, frozen# #) ->
640 (# s3#, J# 1# 1# frozen# #)
644 foreign import stdcall "libHS_cbits.so" "get_stat_st_mode" fileMode :: FileStatus -> FileMode
645 foreign import stdcall "libHS_cbits.so" "prim_S_ISDIR" prim_S_ISDIR :: FileMode -> Int
646 foreign import stdcall "libHS_cbits.so" "prim_S_ISREG" prim_S_ISREG :: FileMode -> Int
648 isDirectory :: FileStatus -> Bool
649 isDirectory stat = prim_S_ISDIR (fileMode stat) /= 0
651 isRegularFile :: FileStatus -> Bool
652 isRegularFile stat = prim_S_ISREG (fileMode stat) /= 0
654 isDirectory :: FileStatus -> Bool
655 isDirectory stat = unsafePerformIO $ do
656 rc <- _casm_ ``%r = S_ISDIR(((struct stat *)%0)->st_mode);'' stat
659 isRegularFile :: FileStatus -> Bool
660 isRegularFile stat = unsafePerformIO $ do
661 rc <- _casm_ ``%r = S_ISREG(((struct stat *)%0)->st_mode);'' stat
670 emptyFileMode :: FileMode
671 unionFileMode :: FileMode -> FileMode -> FileMode
672 intersectFileMode :: FileMode -> FileMode -> FileMode
674 foreign import stdcall "libHS_cbits.so" "const_S_IRUSR" ownerReadMode :: FileMode
675 foreign import stdcall "libHS_cbits.so" "const_S_IWUSR" ownerWriteMode :: FileMode
676 foreign import stdcall "libHS_cbits.so" "const_S_IXUSR" ownerExecuteMode :: FileMode
678 emptyFileMode = primIntToWord 0
679 unionFileMode = primOrWord
680 intersectFileMode = primAndWord
682 ownerReadMode :: FileMode
683 ownerReadMode = ``S_IRUSR''
685 ownerWriteMode :: FileMode
686 ownerWriteMode = ``S_IWUSR''
688 ownerExecuteMode :: FileMode
689 ownerExecuteMode = ``S_IXUSR''
691 intersectFileMode :: FileMode -> FileMode -> FileMode
692 intersectFileMode (W# m1#) (W# m2#) = W# (m1# `and#` m2#)
694 fileMode :: FileStatus -> FileMode
695 fileMode stat = unsafePerformIO (
696 _casm_ ``%r = ((struct stat *)%0)->st_mode;'' stat)