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> #-}
23 Permissions(Permissions),
45 import PrelUnsafe ( unsafePerformIO )
47 import PrelPack ( unpackNBytesST )
48 import PrelForeign ( Word(..) )
50 import Time ( ClockTime(..) )
54 %*********************************************************
56 \subsection{Signatures}
58 %*********************************************************
61 createDirectory :: FilePath -> IO ()
62 removeDirectory :: FilePath -> IO ()
63 removeFile :: FilePath -> IO ()
64 renameDirectory :: FilePath -> FilePath -> IO ()
65 renameFile :: FilePath -> FilePath -> IO ()
66 getDirectoryContents :: FilePath -> IO [FilePath]
67 getCurrentDirectory :: IO FilePath
68 setCurrentDirectory :: FilePath -> IO ()
69 doesFileExist :: FilePath -> IO Bool
70 doesDirectoryExist :: FilePath -> IO Bool
71 getPermissions :: FilePath -> IO Permissions
72 setPermissions :: FilePath -> Permissions -> IO ()
73 getModificationTime :: FilePath -> IO ClockTime
77 %*********************************************************
79 \subsection{Permissions}
81 %*********************************************************
83 The @Permissions@ type is used to record whether certain
84 operations are permissible on a file/directory:
85 [to whom? - owner/group/world - the Report don't say much]
91 executable, searchable :: Bool
92 } deriving (Eq, Ord, Read, Show)
95 %*********************************************************
97 \subsection{Implementation}
99 %*********************************************************
101 @createDirectory dir@ creates a new directory {\em dir} which is
102 initially empty, or as near to empty as the operating system
105 The operation may fail with:
108 \item @isPermissionError@ / @PermissionDenied@
109 The process has insufficient privileges to perform the operation.
111 \item @isAlreadyExistsError@ / @AlreadyExists@
112 The operand refers to a directory that already exists.
114 \item @HardwareFault@
115 A physical I/O error has occurred.
117 \item @InvalidArgument@
118 The operand is not a valid directory name.
119 @[ENAMETOOLONG, ELOOP]@
121 There is no path to the directory.
123 \item @ResourceExhausted@
124 Insufficient resources (virtual memory, process file descriptors,
125 physical disk space, etc.) are available to perform the operation.
126 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
127 \item @InappropriateType@
128 The path refers to an existing non-directory object.
133 createDirectory path = do
134 rc <- _ccall_ createDirectory path
135 if rc == 0 then return () else
136 constructErrorAndFail "createDirectory"
139 @removeDirectory dir@ removes an existing directory {\em dir}. The
140 implementation may specify additional constraints which must be
141 satisfied before a directory can be removed (e.g. the directory has to
142 be empty, or may not be in use by other processes). It is not legal
143 for an implementation to partially remove a directory unless the
144 entire directory is removed. A conformant implementation need not
145 support directory removal in all situations (e.g. removal of the root
148 The operation may fail with:
150 \item @HardwareFault@
151 A physical I/O error has occurred.
153 \item @InvalidArgument@
154 The operand is not a valid directory name.
155 @[ENAMETOOLONG, ELOOP]@
156 \item @isDoesNotExist@ / @NoSuchThing@
157 The directory does not exist.
159 \item @isPermissionError@ / @PermissionDenied@
160 The process has insufficient privileges to perform the operation.
161 @[EROFS, EACCES, EPERM]@
162 \item @UnsatisfiedConstraints@
163 Implementation-dependent constraints are not satisfied.
164 @[EBUSY, ENOTEMPTY, EEXIST]@
165 \item @UnsupportedOperation@
166 The implementation does not support removal in this situation.
168 \item @InappropriateType@
169 The operand refers to an existing non-directory object.
174 removeDirectory path = do
175 rc <- _ccall_ removeDirectory path
179 constructErrorAndFail "removeDirectory"
182 @removeFile file@ removes the directory entry for an existing file
183 {\em file}, where {\em file} is not itself a directory. The
184 implementation may specify additional constraints which must be
185 satisfied before a file can be removed (e.g. the file may not be in
186 use by other processes).
188 The operation may fail with:
190 \item @HardwareFault@
191 A physical I/O error has occurred.
193 \item @InvalidArgument@
194 The operand is not a valid file name.
195 @[ENAMETOOLONG, ELOOP]@
196 \item @isDoesNotExist@ / @NoSuchThing@
197 The file does not exist.
199 \item @isPermissionError@ / @PermissionDenied@
200 The process has insufficient privileges to perform the operation.
201 @[EROFS, EACCES, EPERM]@
202 \item @UnsatisfiedConstraints@
203 Implementation-dependent constraints are not satisfied.
205 \item @InappropriateType@
206 The operand refers to an existing directory.
212 rc <- _ccall_ removeFile path
216 constructErrorAndFail "removeFile"
219 @renameDirectory old@ {\em new} changes the name of an existing
220 directory from {\em old} to {\em new}. If the {\em new} directory
221 already exists, it is atomically replaced by the {\em old} directory.
222 If the {\em new} directory is neither the {\em old} directory nor an
223 alias of the {\em old} directory, it is removed as if by
224 $removeDirectory$. A conformant implementation need not support
225 renaming directories in all situations (e.g. renaming to an existing
226 directory, or across different physical devices), but the constraints
229 The operation may fail with:
231 \item @HardwareFault@
232 A physical I/O error has occurred.
234 \item @InvalidArgument@
235 Either operand is not a valid directory name.
236 @[ENAMETOOLONG, ELOOP]@
237 \item @isDoesNotExistError@ / @NoSuchThing@
238 The original directory does not exist, or there is no path to the target.
240 \item @isPermissionError@ / @PermissionDenied@
241 The process has insufficient privileges to perform the operation.
242 @[EROFS, EACCES, EPERM]@
243 \item @ResourceExhausted@
244 Insufficient resources are available to perform the operation.
245 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
246 \item @UnsatisfiedConstraints@
247 Implementation-dependent constraints are not satisfied.
248 @[EBUSY, ENOTEMPTY, EEXIST]@
249 \item @UnsupportedOperation@
250 The implementation does not support renaming in this situation.
252 \item @InappropriateType@
253 Either path refers to an existing non-directory object.
258 renameDirectory opath npath = do
259 rc <- _ccall_ renameDirectory opath npath
263 constructErrorAndFail "renameDirectory"
266 @renameFile old@ {\em new} changes the name of an existing file system
267 object from {\em old} to {\em new}. If the {\em new} object already
268 exists, it is atomically replaced by the {\em old} object. Neither
269 path may refer to an existing directory. A conformant implementation
270 need not support renaming files in all situations (e.g. renaming
271 across different physical devices), but the constraints must be
274 The operation may fail with:
276 \item @HardwareFault@
277 A physical I/O error has occurred.
279 \item @InvalidArgument@
280 Either operand is not a valid file name.
281 @[ENAMETOOLONG, ELOOP]@
282 \item @isDoesNotExistError@ / @NoSuchThing@
283 The original file does not exist, or there is no path to the target.
285 \item @isPermissionError@ / @PermissionDenied@
286 The process has insufficient privileges to perform the operation.
287 @[EROFS, EACCES, EPERM]@
288 \item @ResourceExhausted@
289 Insufficient resources are available to perform the operation.
290 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
291 \item @UnsatisfiedConstraints@
292 Implementation-dependent constraints are not satisfied.
294 \item @UnsupportedOperation@
295 The implementation does not support renaming in this situation.
297 \item @InappropriateType@
298 Either path refers to an existing directory.
299 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
303 renameFile opath npath = do
304 rc <- _ccall_ renameFile opath npath
308 constructErrorAndFail "renameFile"
311 @getDirectoryContents dir@ returns a list of {\em all} entries
314 The operation may fail with:
316 \item @HardwareFault@
317 A physical I/O error has occurred.
319 \item @InvalidArgument@
320 The operand is not a valid directory name.
321 @[ENAMETOOLONG, ELOOP]@
322 \item @isDoesNotExistError@ / @NoSuchThing@
323 The directory does not exist.
325 \item @isPermissionError@ / @PermissionDenied@
326 The process has insufficient privileges to perform the operation.
328 \item @ResourceExhausted@
329 Insufficient resources are available to perform the operation.
331 \item @InappropriateType@
332 The path refers to an existing non-directory object.
337 --getDirectoryContents :: FilePath -> IO [FilePath]
338 getDirectoryContents path = do
339 dir <- _ccall_ openDir__ path
340 ptr <- _ccall_ malloc (``sizeof(struct dirent**)''::Int)
342 then constructErrorAndFail "getDirectoryContents"
345 loop :: Addr -> Addr -> IO [String]
346 loop dir dirent_ptr = do
347 dirent_ptr <- _ccall_ readDir__ dir
348 if dirent_ptr == ``NULL''
352 str <- _casm_ `` %r=(char*)((struct dirent*)%0)->d_name; '' dirent_ptr
353 -- not using the unpackCString function here, since we have to force
354 -- the unmarshalling of the directory entry right here as subsequent
355 -- calls to readdir() may overwrite it.
356 len <- _ccall_ strlen str
357 entry <- stToIO (unpackNBytesST str len)
358 entries <- loop dir dirent_ptr
359 return (entry:entries)
362 If the operating system has a notion of current directories,
363 @getCurrentDirectory@ returns an absolute path to the
364 current directory of the calling process.
366 The operation may fail with:
368 \item @HardwareFault@
369 A physical I/O error has occurred.
371 \item @isDoesNotExistError@ / @NoSuchThing@
372 There is no path referring to the current directory.
373 @[EPERM, ENOENT, ESTALE...]@
374 \item @isPermissionError@ / @PermissionDenied@
375 The process has insufficient privileges to perform the operation.
377 \item @ResourceExhausted@
378 Insufficient resources are available to perform the operation.
379 \item @UnsupportedOperation@
380 The operating system has no notion of current directory.
384 getCurrentDirectory = do
385 str <- _ccall_ getCurrentDirectory
388 len <- _ccall_ strlen str
389 pwd <- stToIO (unpackNBytesST str len)
393 constructErrorAndFail "getCurrentDirectory"
396 If the operating system has a notion of current directories,
397 @setCurrentDirectory dir@ changes the current
398 directory of the calling process to {\em dir}.
400 The operation may fail with:
402 \item @HardwareFault@
403 A physical I/O error has occurred.
405 \item @InvalidArgument@
406 The operand is not a valid directory name.
407 @[ENAMETOOLONG, ELOOP]@
408 \item @isDoesNotExistError@ / @NoSuchThing@
409 The directory does not exist.
411 \item @isPermissionError@ / @PermissionDenied@
412 The process has insufficient privileges to perform the operation.
414 \item @UnsupportedOperation@
415 The operating system has no notion of current directory, or the
416 current directory cannot be dynamically changed.
417 \item @InappropriateType@
418 The path refers to an existing non-directory object.
423 setCurrentDirectory path = do
424 rc <- _ccall_ setCurrentDirectory path
427 else constructErrorAndFail "setCurrentDirectory"
432 --doesFileExist :: FilePath -> IO Bool
433 doesFileExist name = do
434 rc <- _ccall_ access name (``F_OK''::Int)
437 --doesDirectoryExist :: FilePath -> IO Bool
438 doesDirectoryExist name =
439 (getFileStatus name >>= \ st -> return (isDirectory st))
441 (\ _ -> return False)
443 --getModificationTime :: FilePath -> IO ClockTime
444 getModificationTime name =
445 getFileStatus name >>= \ st ->
448 --getPermissions :: FilePath -> IO Permissions
449 getPermissions name =
450 getFileStatus name >>= \ st ->
453 isect v = intersectFileMode v fm == v
457 readable = isect ownerReadMode,
458 writeable = isect ownerWriteMode,
459 executable = not (isDirectory st) && isect ownerExecuteMode,
460 searchable = not (isRegularFile st) && isect ownerExecuteMode
464 --setPermissions :: FilePath -> Permissions -> IO ()
465 setPermissions name (Permissions r w e s) = do
467 read# = case (if r then ownerReadMode else ``0'') of { W# x# -> x# }
468 write# = case (if w then ownerWriteMode else ``0'') of { W# x# -> x# }
469 exec# = case (if e || s then ownerExecuteMode else ``0'') of { W# x# -> x# }
471 mode = I# (word2Int# (read# `or#` write# `or#` exec#))
473 rc <- _ccall_ chmod name mode
476 else 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
487 getFileStatus name = do
488 bytes <- stToIO (newCharArray (0,``sizeof(struct stat)''))
489 rc <- _casm_ ``%r = stat(%0,(struct stat *)%1);'' name bytes
491 then stToIO (unsafeFreezeByteArray bytes)
492 else fail (IOError Nothing SystemError "Directory.getFileStatus")
494 modificationTime :: FileStatus -> IO ClockTime
495 modificationTime stat = do
497 _casm_ ``((unsigned long *)%1)[0] = ((struct stat *)%0)->st_mtime;'' stat i1
498 secs <- cvtUnsigned i1
501 malloc1 = IO $ \ s# ->
502 case newIntArray# 1# s# of
503 StateAndMutableByteArray# s2# barr# ->
504 IOok s2# (MutableByteArray bnds barr#)
507 -- The C routine fills in an unsigned word. We don't have `unsigned2Integer#,'
508 -- so we freeze the data bits and use them for an MP_INT structure. Note that
509 -- zero is still handled specially, although (J# 1# 1# (ptr to 0#)) is probably
510 -- acceptable to gmp.
512 cvtUnsigned (MutableByteArray _ arr#) = IO $ \ s# ->
513 case readIntArray# arr# 0# s# of
514 StateAndInt# s2# r# ->
518 case unsafeFreezeByteArray# arr# s2# of
519 StateAndByteArray# s3# frozen# ->
520 IOok s3# (J# 1# 1# frozen#)
522 isDirectory :: FileStatus -> Bool
523 isDirectory stat = unsafePerformIO $ do
524 rc <- _casm_ ``%r = S_ISDIR(((struct stat *)%0)->st_mode);'' stat
527 isRegularFile :: FileStatus -> Bool
528 isRegularFile stat = unsafePerformIO $ do
529 rc <- _casm_ ``%r = S_ISREG(((struct stat *)%0)->st_mode);'' stat
535 ownerReadMode :: FileMode
536 ownerReadMode = ``S_IRUSR''
538 ownerWriteMode :: FileMode
539 ownerWriteMode = ``S_IWUSR''
541 ownerExecuteMode :: FileMode
542 ownerExecuteMode = ``S_IXUSR''
544 intersectFileMode :: FileMode -> FileMode -> FileMode
545 intersectFileMode (W# m1#) (W# m2#) = W# (m1# `and#` m2#)
547 fileMode :: FileStatus -> FileMode
548 fileMode stat = unsafePerformIO (
549 _casm_ ``%r = ((struct stat *)%0)->st_mode;'' stat)