1 -----------------------------------------------------------------------------
3 -- Module : System.Directory
4 -- Copyright : (c) The University of Glasgow 2001
5 -- License : BSD-style (see the file libraries/base/LICENSE)
7 -- Maintainer : libraries@haskell.org
8 -- Stability : provisional
9 -- Portability : portable
11 -- System-independent interface to directory manipulation.
13 -----------------------------------------------------------------------------
15 module System.Directory
25 readable, -- :: Permissions -> Bool
26 writable, -- :: Permissions -> Bool
27 executable, -- :: Permissions -> Bool
28 searchable -- :: Permissions -> Bool
31 -- * Actions on directories
32 , createDirectory -- :: FilePath -> IO ()
33 , removeDirectory -- :: FilePath -> IO ()
34 , renameDirectory -- :: FilePath -> FilePath -> IO ()
36 , getDirectoryContents -- :: FilePath -> IO [FilePath]
37 , getCurrentDirectory -- :: IO FilePath
38 , setCurrentDirectory -- :: FilePath -> IO ()
41 , removeFile -- :: FilePath -> IO ()
42 , renameFile -- :: FilePath -> FilePath -> IO ()
45 , doesFileExist -- :: FilePath -> IO Bool
46 , doesDirectoryExist -- :: FilePath -> IO Bool
48 -- * Setting and retrieving permissions
50 , getPermissions -- :: FilePath -> IO Permissions
51 , setPermissions -- :: FilePath -> Permissions -> IO ()
55 , getModificationTime -- :: FilePath -> IO ClockTime
60 import System.Time ( ClockTime(..) )
65 #ifdef __GLASGOW_HASKELL__
67 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
71 A directory contains a series of entries, each of which is a named
72 reference to a file system object (file, directory etc.). Some
73 entries may be hidden, inaccessible, or have some administrative
74 function (e.g. `.' or `..' under POSIX
75 <http://www.opengroup.org/onlinepubs/007904975/toc.htm>), but in
76 this standard all such entries are considered to form part of the
77 directory contents. Entries in sub-directories are not, however,
78 considered to form part of the directory contents.
80 Each file system object is referenced by a /path/. There is
81 normally at least one absolute path to each file system object. In
82 some operating systems, it may also be possible to have paths which
83 are relative to the current directory.
86 -----------------------------------------------------------------------------
91 The 'Permissions' type is used to record whether certain operations are
92 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
93 get and set these permissions, respectively. Permissions apply both to
94 files and directories. For directories, the executable field will be
95 'False', and for files the searchable field will be 'False'. Note that
96 directories may be searchable without being readable, if permission has
97 been given to use them as part of a path, but not to examine the
100 Note that to change some, but not all permissions, a construct on the following lines must be used.
102 > makeReadable f = do
103 > p <- getPermissions f
104 > setPermissions f (p {readable = True})
111 executable, searchable :: Bool
112 } deriving (Eq, Ord, Read, Show)
114 getPermissions :: FilePath -> IO Permissions
115 getPermissions name = do
116 withCString name $ \s -> do
117 read <- c_access s r_OK
118 write <- c_access s w_OK
119 exec <- c_access s x_OK
120 withFileStatus name $ \st -> do
121 is_dir <- isDirectory st
124 readable = read == 0,
125 writable = write == 0,
126 executable = not is_dir && exec == 0,
127 searchable = is_dir && exec == 0
131 setPermissions :: FilePath -> Permissions -> IO ()
132 setPermissions name (Permissions r w e s) = do
134 read = if r then s_IRUSR else emptyCMode
135 write = if w then s_IWUSR else emptyCMode
136 exec = if e || s then s_IXUSR else emptyCMode
138 mode = read `unionCMode` (write `unionCMode` exec)
140 withCString name $ \s ->
141 throwErrnoIfMinus1_ "setPermissions" $ c_chmod s mode
143 -----------------------------------------------------------------------------
146 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
147 initially empty, or as near to empty as the operating system
150 The operation may fail with:
152 * 'isPermissionError' \/ 'PermissionDenied'
153 The process has insufficient privileges to perform the operation.
156 * 'isAlreadyExistsError' \/ 'AlreadyExists'
157 The operand refers to a directory that already exists.
161 A physical I\/O error has occurred.
165 The operand is not a valid directory name.
166 @[ENAMETOOLONG, ELOOP]@
169 There is no path to the directory.
172 * 'ResourceExhausted'
173 Insufficient resources (virtual memory, process file descriptors,
174 physical disk space, etc.) are available to perform the operation.
175 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
177 * 'InappropriateType'
178 The path refers to an existing non-directory object.
183 createDirectory :: FilePath -> IO ()
184 createDirectory path = do
185 withCString path $ \s -> do
186 throwErrnoIfMinus1Retry_ "createDirectory" $
189 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
190 implementation may specify additional constraints which must be
191 satisfied before a directory can be removed (e.g. the directory has to
192 be empty, or may not be in use by other processes). It is not legal
193 for an implementation to partially remove a directory unless the
194 entire directory is removed. A conformant implementation need not
195 support directory removal in all situations (e.g. removal of the root
198 The operation may fail with:
201 A physical I\/O error has occurred.
205 The operand is not a valid directory name.
206 [ENAMETOOLONG, ELOOP]
208 * 'isDoesNotExist' 'NoSuchThing'
209 The directory does not exist.
212 * 'isPermissionError' \/ 'PermissionDenied'
213 The process has insufficient privileges to perform the operation.
214 @[EROFS, EACCES, EPERM]@
216 * 'UnsatisfiedConstraints'
217 Implementation-dependent constraints are not satisfied.
218 @[EBUSY, ENOTEMPTY, EEXIST]@
220 * 'UnsupportedOperation'
221 The implementation does not support removal in this situation.
224 * 'InappropriateType'
225 The operand refers to an existing non-directory object.
230 removeDirectory :: FilePath -> IO ()
231 removeDirectory path = do
232 withCString path $ \s ->
233 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
235 {- |@'removefile' file@ removes the directory entry for an existing file
236 /file/, where /file/ is not itself a directory. The
237 implementation may specify additional constraints which must be
238 satisfied before a file can be removed (e.g. the file may not be in
239 use by other processes).
241 The operation may fail with:
244 A physical I\/O error has occurred.
248 The operand is not a valid file name.
249 @[ENAMETOOLONG, ELOOP]@
251 * 'isDoesNotExist' \/ 'NoSuchThing'
252 The file does not exist.
255 * 'isPermissionError' \/ 'PermissionDenied'
256 The process has insufficient privileges to perform the operation.
257 @[EROFS, EACCES, EPERM]@
259 * 'UnsatisfiedConstraints'
260 Implementation-dependent constraints are not satisfied.
263 * 'InappropriateType'
264 The operand refers to an existing directory.
269 removeFile :: FilePath -> IO ()
271 withCString path $ \s ->
272 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
274 {- |@'renameDirectory' old new@ changes the name of an existing
275 directory from /old/ to /new/. If the /new/ directory
276 already exists, it is atomically replaced by the /old/ directory.
277 If the /new/ directory is neither the /old/ directory nor an
278 alias of the /old/ directory, it is removed as if by
279 'removeDirectory'. A conformant implementation need not support
280 renaming directories in all situations (e.g. renaming to an existing
281 directory, or across different physical devices), but the constraints
284 The operation may fail with:
287 A physical I\/O error has occurred.
291 Either operand is not a valid directory name.
292 @[ENAMETOOLONG, ELOOP]@
294 * 'isDoesNotExistError' \/ 'NoSuchThing'
295 The original directory does not exist, or there is no path to the target.
298 * 'isPermissionError' \/ 'PermissionDenied'
299 The process has insufficient privileges to perform the operation.
300 @[EROFS, EACCES, EPERM]@
302 * 'ResourceExhausted'
303 Insufficient resources are available to perform the operation.
304 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
306 * 'UnsatisfiedConstraints'
307 Implementation-dependent constraints are not satisfied.
308 @[EBUSY, ENOTEMPTY, EEXIST]@
310 * 'UnsupportedOperation'
311 The implementation does not support renaming in this situation.
314 * 'InappropriateType'
315 Either path refers to an existing non-directory object.
320 renameDirectory :: FilePath -> FilePath -> IO ()
321 renameDirectory opath npath =
322 withFileStatus opath $ \st -> do
323 is_dir <- isDirectory st
325 then ioException (IOError Nothing InappropriateType "renameDirectory"
326 ("not a directory") (Just opath))
329 withCString opath $ \s1 ->
330 withCString npath $ \s2 ->
331 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
333 {- |@'renameFile' old new@ changes the name of an existing file system
334 object from /old/ to /new/. If the /new/ object already
335 exists, it is atomically replaced by the /old/ object. Neither
336 path may refer to an existing directory. A conformant implementation
337 need not support renaming files in all situations (e.g. renaming
338 across different physical devices), but the constraints must be
341 The operation may fail with:
344 A physical I\/O error has occurred.
348 Either operand is not a valid file name.
349 @[ENAMETOOLONG, ELOOP]@
351 * 'isDoesNotExistError' \/ 'NoSuchThing'
352 The original file does not exist, or there is no path to the target.
355 * 'isPermissionError' \/ 'PermissionDenied'
356 The process has insufficient privileges to perform the operation.
357 @[EROFS, EACCES, EPERM]@
359 * 'ResourceExhausted'
360 Insufficient resources are available to perform the operation.
361 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
363 * 'UnsatisfiedConstraints'
364 Implementation-dependent constraints are not satisfied.
367 * 'UnsupportedOperation'
368 The implementation does not support renaming in this situation.
371 * 'InappropriateType'
372 Either path refers to an existing directory.
373 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
377 renameFile :: FilePath -> FilePath -> IO ()
378 renameFile opath npath =
379 withFileOrSymlinkStatus opath $ \st -> do
380 is_dir <- isDirectory st
382 then ioException (IOError Nothing InappropriateType "renameFile"
383 "is a directory" (Just opath))
386 withCString opath $ \s1 ->
387 withCString npath $ \s2 ->
388 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
390 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
393 The operation may fail with:
396 A physical I\/O error has occurred.
400 The operand is not a valid directory name.
401 @[ENAMETOOLONG, ELOOP]@
403 * 'isDoesNotExistError' \/ 'NoSuchThing'
404 The directory does not exist.
407 * 'isPermissionError' \/ 'PermissionDenied'
408 The process has insufficient privileges to perform the operation.
411 * 'ResourceExhausted'
412 Insufficient resources are available to perform the operation.
415 * 'InappropriateType'
416 The path refers to an existing non-directory object.
421 getDirectoryContents :: FilePath -> IO [FilePath]
422 getDirectoryContents path = do
423 alloca $ \ ptr_dEnt ->
425 (withCString path $ \s ->
426 throwErrnoIfNullRetry desc (c_opendir s))
427 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
428 (\p -> loop ptr_dEnt p)
430 desc = "getDirectoryContents"
432 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
433 loop ptr_dEnt dir = do
435 r <- readdir dir ptr_dEnt
438 dEnt <- peek ptr_dEnt
442 entry <- (d_name dEnt >>= peekCString)
444 entries <- loop ptr_dEnt dir
445 return (entry:entries)
446 else do errno <- getErrno
447 if (errno == eINTR) then loop ptr_dEnt dir else do
448 let (Errno eo) = errno
449 if (eo == end_of_dir)
455 {- |If the operating system has a notion of current directories,
456 'getCurrentDirectory' returns an absolute path to the
457 current directory of the calling process.
459 The operation may fail with:
462 A physical I\/O error has occurred.
465 * 'isDoesNotExistError' \/ 'NoSuchThing'
466 There is no path referring to the current directory.
467 @[EPERM, ENOENT, ESTALE...]@
469 * 'isPermissionError' \/ 'PermissionDenied'
470 The process has insufficient privileges to perform the operation.
473 * 'ResourceExhausted'
474 Insufficient resources are available to perform the operation.
476 * 'UnsupportedOperation'
477 The operating system has no notion of current directory.
481 getCurrentDirectory :: IO FilePath
482 getCurrentDirectory = do
483 p <- mallocBytes path_max
485 where go p bytes = do
486 p' <- c_getcwd p (fromIntegral bytes)
488 then do s <- peekCString p'
491 else do errno <- getErrno
493 then do let bytes' = bytes * 2
494 p' <- reallocBytes p bytes'
496 else throwErrno "getCurrentDirectory"
498 {- |If the operating system has a notion of current directories,
499 @'setCurrentDirectory' dir@ changes the current
500 directory of the calling process to /dir/.
502 The operation may fail with:
505 A physical I\/O error has occurred.
509 The operand is not a valid directory name.
510 @[ENAMETOOLONG, ELOOP]@
512 * 'isDoesNotExistError' \/ 'NoSuchThing'
513 The directory does not exist.
516 * 'isPermissionError' \/ 'PermissionDenied'
517 The process has insufficient privileges to perform the operation.
520 * 'UnsupportedOperation'
521 The operating system has no notion of current directory, or the
522 current directory cannot be dynamically changed.
524 * 'InappropriateType'
525 The path refers to an existing non-directory object.
530 setCurrentDirectory :: FilePath -> IO ()
531 setCurrentDirectory path = do
532 withCString path $ \s ->
533 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
534 -- ToDo: add path to error
536 {- |To clarify, 'doesDirectoryExist' returns 'True' if a file system object
537 exist, and it's a directory. 'doesFileExist' returns 'True' if the file
538 system object exist, but it's not a directory (i.e., for every other
539 file system object that is not a directory.)
542 doesDirectoryExist :: FilePath -> IO Bool
543 doesDirectoryExist name =
545 (withFileStatus name $ \st -> isDirectory st)
546 (\ _ -> return False)
548 doesFileExist :: FilePath -> IO Bool
549 doesFileExist name = do
551 (withFileStatus name $ \st -> do b <- isDirectory st; return (not b))
552 (\ _ -> return False)
554 getModificationTime :: FilePath -> IO ClockTime
555 getModificationTime name =
556 withFileStatus name $ \ st ->
559 withFileStatus :: FilePath -> (Ptr CStat -> IO a) -> IO a
560 withFileStatus name f = do
561 allocaBytes sizeof_stat $ \p ->
562 withCString name $ \s -> do
563 throwErrnoIfMinus1Retry_ "withFileStatus" (c_stat s p)
566 withFileOrSymlinkStatus :: FilePath -> (Ptr CStat -> IO a) -> IO a
567 withFileOrSymlinkStatus name f = do
568 allocaBytes sizeof_stat $ \p ->
569 withCString name $ \s -> do
570 throwErrnoIfMinus1Retry_ "withFileOrSymlinkStatus" (lstat s p)
573 modificationTime :: Ptr CStat -> IO ClockTime
574 modificationTime stat = do
575 mtime <- st_mtime stat
576 return (TOD (toInteger (mtime :: CTime)) 0)
578 isDirectory :: Ptr CStat -> IO Bool
579 isDirectory stat = do
581 return (s_isdir mode)
586 unionCMode :: CMode -> CMode -> CMode
590 foreign import ccall unsafe "__hscore_path_max"
593 foreign import ccall unsafe "__hscore_readdir"
594 readdir :: Ptr CDir -> Ptr (Ptr CDirent) -> IO CInt
596 foreign import ccall unsafe "__hscore_free_dirent"
597 freeDirEnt :: Ptr CDirent -> IO ()
599 foreign import ccall unsafe "__hscore_end_of_dir"
602 foreign import ccall unsafe "__hscore_d_name"
603 d_name :: Ptr CDirent -> IO CString
605 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
606 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
607 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
609 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
610 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
611 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode