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> #-}
23 Permissions(Permissions),
46 import UnsafeST ( unsafePerformPrimIO )
48 import PackBase ( unpackNBytesST )
49 import Time ( ClockTime(..) )
53 %*********************************************************
55 \subsection{Signatures}
57 %*********************************************************
60 createDirectory :: FilePath -> IO ()
61 removeDirectory :: FilePath -> IO ()
62 removeFile :: FilePath -> IO ()
63 renameDirectory :: FilePath -> FilePath -> IO ()
64 renameFile :: FilePath -> FilePath -> IO ()
65 getDirectoryContents :: FilePath -> IO [FilePath]
66 getCurrentDirectory :: IO FilePath
67 setCurrentDirectory :: FilePath -> IO ()
68 doesFileExist :: FilePath -> IO Bool
69 doesDirectoryExist :: FilePath -> IO Bool
70 getPermissions :: FilePath -> IO Permissions
71 setPermissions :: FilePath -> Permissions -> IO ()
72 getModificationTime :: FilePath -> IO ClockTime
76 %*********************************************************
78 \subsection{Permissions}
80 %*********************************************************
82 The @Permissions@ type is used to record whether certain
83 operations are permissible on a file/directory:
84 [to whom? - owner/group/world - the Report don't say much]
90 executable, searchable :: Bool
91 } deriving (Eq, Ord, Read, Show)
94 %*********************************************************
96 \subsection{Implementation}
98 %*********************************************************
100 @createDirectory dir@ creates a new directory {\em dir} which is
101 initially empty, or as near to empty as the operating system
104 The operation may fail with:
107 \item @isPermissionError@ / @PermissionDenied@
108 The process has insufficient privileges to perform the operation.
110 \item @isAlreadyExistsError@ / @AlreadyExists@
111 The operand refers to a directory that already exists.
113 \item @HardwareFault@
114 A physical I/O error has occurred.
116 \item @InvalidArgument@
117 The operand is not a valid directory name.
118 @[ENAMETOOLONG, ELOOP]@
120 There is no path to the directory.
122 \item @ResourceExhausted@
123 Insufficient resources (virtual memory, process file descriptors,
124 physical disk space, etc.) are available to perform the operation.
125 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
126 \item @InappropriateType@
127 The path refers to an existing non-directory object.
132 createDirectory path =
133 _ccall_ createDirectory path `thenIO_Prim` \ rc ->
137 constructErrorAndFail "createDirectory"
140 @removeDirectory dir@ removes an existing directory {\em dir}. The
141 implementation may specify additional constraints which must be
142 satisfied before a directory can be removed (e.g. the directory has to
143 be empty, or may not be in use by other processes). It is not legal
144 for an implementation to partially remove a directory unless the
145 entire directory is removed. A conformant implementation need not
146 support directory removal in all situations (e.g. removal of the root
149 The operation may fail with:
151 \item @HardwareFault@
152 A physical I/O error has occurred.
154 \item @InvalidArgument@
155 The operand is not a valid directory name.
156 @[ENAMETOOLONG, ELOOP]@
157 \item @isDoesNotExist@ / @NoSuchThing@
158 The directory does not exist.
160 \item @isPermissionError@ / @PermissionDenied@
161 The process has insufficient privileges to perform the operation.
162 @[EROFS, EACCES, EPERM]@
163 \item @UnsatisfiedConstraints@
164 Implementation-dependent constraints are not satisfied.
165 @[EBUSY, ENOTEMPTY, EEXIST]@
166 \item @UnsupportedOperation@
167 The implementation does not support removal in this situation.
169 \item @InappropriateType@
170 The operand refers to an existing non-directory object.
175 removeDirectory path =
176 _ccall_ removeDirectory path `thenIO_Prim` \ rc ->
180 constructErrorAndFail "removeDirectory"
183 @removeFile file@ removes the directory entry for an existing file
184 {\em file}, where {\em file} is not itself a directory. The
185 implementation may specify additional constraints which must be
186 satisfied before a file can be removed (e.g. the file may not be in
187 use by other processes).
189 The operation may fail with:
191 \item @HardwareFault@
192 A physical I/O error has occurred.
194 \item @InvalidArgument@
195 The operand is not a valid file name.
196 @[ENAMETOOLONG, ELOOP]@
197 \item @isDoesNotExist@ / @NoSuchThing@
198 The file does not exist.
200 \item @isPermissionError@ / @PermissionDenied@
201 The process has insufficient privileges to perform the operation.
202 @[EROFS, EACCES, EPERM]@
203 \item @UnsatisfiedConstraints@
204 Implementation-dependent constraints are not satisfied.
206 \item @InappropriateType@
207 The operand refers to an existing directory.
213 _ccall_ removeFile path `thenIO_Prim` \ rc ->
217 constructErrorAndFail "removeFile"
220 @renameDirectory old@ {\em new} changes the name of an existing
221 directory from {\em old} to {\em new}. If the {\em new} directory
222 already exists, it is atomically replaced by the {\em old} directory.
223 If the {\em new} directory is neither the {\em old} directory nor an
224 alias of the {\em old} directory, it is removed as if by
225 $removeDirectory$. A conformant implementation need not support
226 renaming directories in all situations (e.g. renaming to an existing
227 directory, or across different physical devices), but the constraints
230 The operation may fail with:
232 \item @HardwareFault@
233 A physical I/O error has occurred.
235 \item @InvalidArgument@
236 Either operand is not a valid directory name.
237 @[ENAMETOOLONG, ELOOP]@
238 \item @isDoesNotExistError@ / @NoSuchThing@
239 The original directory does not exist, or there is no path to the target.
241 \item @isPermissionError@ / @PermissionDenied@
242 The process has insufficient privileges to perform the operation.
243 @[EROFS, EACCES, EPERM]@
244 \item @ResourceExhausted@
245 Insufficient resources are available to perform the operation.
246 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
247 \item @UnsatisfiedConstraints@
248 Implementation-dependent constraints are not satisfied.
249 @[EBUSY, ENOTEMPTY, EEXIST]@
250 \item @UnsupportedOperation@
251 The implementation does not support renaming in this situation.
253 \item @InappropriateType@
254 Either path refers to an existing non-directory object.
259 renameDirectory opath npath =
260 _ccall_ renameDirectory opath npath `thenIO_Prim` \ rc ->
264 constructErrorAndFail "renameDirectory"
267 @renameFile old@ {\em new} changes the name of an existing file system
268 object from {\em old} to {\em new}. If the {\em new} object already
269 exists, it is atomically replaced by the {\em old} object. Neither
270 path may refer to an existing directory. A conformant implementation
271 need not support renaming files in all situations (e.g. renaming
272 across different physical devices), but the constraints must be
275 The operation may fail with:
277 \item @HardwareFault@
278 A physical I/O error has occurred.
280 \item @InvalidArgument@
281 Either operand is not a valid file name.
282 @[ENAMETOOLONG, ELOOP]@
283 \item @isDoesNotExistError@ / @NoSuchThing@
284 The original file does not exist, or there is no path to the target.
286 \item @isPermissionError@ / @PermissionDenied@
287 The process has insufficient privileges to perform the operation.
288 @[EROFS, EACCES, EPERM]@
289 \item @ResourceExhausted@
290 Insufficient resources are available to perform the operation.
291 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
292 \item @UnsatisfiedConstraints@
293 Implementation-dependent constraints are not satisfied.
295 \item @UnsupportedOperation@
296 The implementation does not support renaming in this situation.
298 \item @InappropriateType@
299 Either path refers to an existing directory.
300 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
304 renameFile opath npath =
305 _ccall_ renameFile opath npath `thenIO_Prim` \ rc ->
309 constructErrorAndFail "renameFile"
312 @getDirectoryContents dir@ returns a list of {\em all} entries
315 The operation may fail with:
317 \item @HardwareFault@
318 A physical I/O error has occurred.
320 \item @InvalidArgument@
321 The operand is not a valid directory name.
322 @[ENAMETOOLONG, ELOOP]@
323 \item @isDoesNotExistError@ / @NoSuchThing@
324 The directory does not exist.
326 \item @isPermissionError@ / @PermissionDenied@
327 The process has insufficient privileges to perform the operation.
329 \item @ResourceExhausted@
330 Insufficient resources are available to perform the operation.
332 \item @InappropriateType@
333 The path refers to an existing non-directory object.
338 getDirectoryContents path =
339 _ccall_ getDirectoryContents path `thenIO_Prim` \ ptr ->
340 if ptr == ``NULL'' then
341 constructErrorAndFail "getDirectoryContents"
343 stToIO (getEntries ptr 0) >>= \ entries ->
344 _ccall_ free ptr `thenIO_Prim` \ () ->
347 getEntries :: Addr -> Int -> PrimIO [FilePath]
349 _casm_ ``%r = ((char **)%0)[%1];'' ptr n >>= \ str ->
350 if str == ``NULL'' then
353 _ccall_ strlen str >>= \ len ->
354 unpackNBytesST str len >>= \ entry ->
355 _ccall_ free str >>= \ () ->
356 getEntries ptr (n+1) >>= \ entries ->
357 return (entry : entries)
360 If the operating system has a notion of current directories,
361 @getCurrentDirectory@ returns an absolute path to the
362 current directory of the calling process.
364 The operation may fail with:
366 \item @HardwareFault@
367 A physical I/O error has occurred.
369 \item @isDoesNotExistError@ / @NoSuchThing@
370 There is no path referring to the current directory.
371 @[EPERM, ENOENT, ESTALE...]@
372 \item @isPermissionError@ / @PermissionDenied@
373 The process has insufficient privileges to perform the operation.
375 \item @ResourceExhausted@
376 Insufficient resources are available to perform the operation.
377 \item @UnsupportedOperation@
378 The operating system has no notion of current directory.
382 getCurrentDirectory =
383 _ccall_ getCurrentDirectory `thenIO_Prim` \ str ->
384 if str /= ``NULL'' then
385 _ccall_ strlen str `thenIO_Prim` \ len ->
386 stToIO (unpackNBytesST len str) >>= \ pwd ->
387 _ccall_ free str `thenIO_Prim` \ () ->
390 constructErrorAndFail "getCurrentDirectory"
393 If the operating system has a notion of current directories,
394 @setCurrentDirectory dir@ changes the current
395 directory of the calling process to {\em dir}.
397 The operation may fail with:
399 \item @HardwareFault@
400 A physical I/O error has occurred.
402 \item @InvalidArgument@
403 The operand is not a valid directory name.
404 @[ENAMETOOLONG, ELOOP]@
405 \item @isDoesNotExistError@ / @NoSuchThing@
406 The directory does not exist.
408 \item @isPermissionError@ / @PermissionDenied@
409 The process has insufficient privileges to perform the operation.
411 \item @UnsupportedOperation@
412 The operating system has no notion of current directory, or the
413 current directory cannot be dynamically changed.
414 \item @InappropriateType@
415 The path refers to an existing non-directory object.
420 setCurrentDirectory path =
421 _ccall_ setCurrentDirectory path `thenIO_Prim` \ rc ->
425 constructErrorAndFail "setCurrentDirectory"
431 --doesFileExist :: FilePath -> IO Bool
433 _ccall_ access name (``F_OK''::Int) `thenIO_Prim` \ rc ->
436 --doesDirectoryExist :: FilePath -> IO Bool
437 doesDirectoryExist name =
438 (getFileStatus name >>= \ st -> return (isDirectory st))
440 (\ _ -> return False)
442 --getModificationTime :: FilePath -> IO ClockTime
443 getModificationTime name =
444 getFileStatus name >>= \ st ->
447 --getPermissions :: FilePath -> IO Permissions
448 getPermissions name =
449 getFileStatus name >>= \ st ->
452 isect v = intersectFileMode v fm == v
456 readable = isect ownerReadMode,
457 writeable = isect ownerWriteMode,
458 executable = not (isDirectory st) && isect ownerExecuteMode,
459 searchable = not (isRegularFile st) && isect ownerExecuteMode
463 --setPermissions :: FilePath -> Permissions -> IO ()
464 setPermissions name (Permissions r w e s) =
466 read# = case (if r then ownerReadMode else ``0'') of { W# x# -> x# }
467 write# = case (if w then ownerWriteMode else ``0'') of { W# x# -> x# }
468 exec# = case (if e || s then ownerExecuteMode else ``0'') of { W# x# -> x# }
470 mode = I# (word2Int# (read# `or#` write# `or#` exec#))
472 _ccall_ chmod name mode `thenIO_Prim` \ rc ->
476 fail (IOError Nothing SystemError "Directory.setPermissions")
481 (Sigh)..copied from Posix.Files to avoid dep. on posix library
484 type FileStatus = ByteArray Int
486 getFileStatus :: FilePath -> IO FileStatus
488 newCharArray (0,``sizeof(struct stat)'') `thenIO_Prim` \ bytes ->
489 _casm_ ``%r = stat(%0,(struct stat *)%1);'' name bytes
490 `thenIO_Prim` \ rc ->
492 unsafeFreezeByteArray bytes `thenIO_Prim` \ stat ->
495 fail (IOError Nothing SystemError "Directory.getFileStatus")
497 modificationTime :: FileStatus -> IO ClockTime
498 modificationTime stat =
499 malloc1 `thenIO_Prim` \ i1 ->
500 _casm_ ``((unsigned long *)%1)[0] = ((struct stat *)%0)->st_mtime;'' stat i1 `thenIO_Prim` \ () ->
501 cvtUnsigned i1 `thenIO_Prim` \ secs ->
504 malloc1 = ST $ \ (S# s#) ->
505 case newIntArray# 1# s# of
506 StateAndMutableByteArray# s2# barr# -> (MutableByteArray bnds barr#, S# s2#)
509 -- The C routine fills in an unsigned word. We don't have `unsigned2Integer#,'
510 -- so we freeze the data bits and use them for an MP_INT structure. Note that
511 -- zero is still handled specially, although (J# 1# 1# (ptr to 0#)) is probably
512 -- acceptable to gmp.
514 cvtUnsigned (MutableByteArray _ arr#) = ST $ \ (S# s#) ->
515 case readIntArray# arr# 0# s# of
516 StateAndInt# s2# r# ->
520 case unsafeFreezeByteArray# arr# s2# of
521 StateAndByteArray# s3# frozen# -> (J# 1# 1# frozen#, S# s3#)
523 isDirectory :: FileStatus -> Bool
524 isDirectory stat = unsafePerformPrimIO $
525 _casm_ ``%r = S_ISDIR(((struct stat *)%0)->st_mode);'' stat >>= \ rc ->
528 isRegularFile :: FileStatus -> Bool
529 isRegularFile stat = unsafePerformPrimIO $
530 _casm_ ``%r = S_ISREG(((struct stat *)%0)->st_mode);'' stat >>= \ rc ->
538 ownerReadMode :: FileMode
539 ownerReadMode = ``S_IRUSR''
541 ownerWriteMode :: FileMode
542 ownerWriteMode = ``S_IWUSR''
544 ownerExecuteMode :: FileMode
545 ownerExecuteMode = ``S_IXUSR''
547 intersectFileMode :: FileMode -> FileMode -> FileMode
548 intersectFileMode (W# m1#) (W# m2#) = W# (m1# `and#` m2#)
550 fileMode :: FileStatus -> FileMode
551 fileMode stat = unsafePerformPrimIO $
552 _casm_ ``%r = ((struct stat *)%0)->st_mode;'' stat >>= \ mode ->