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
64 import Control.Exception ( bracket )
65 import System.Posix.Types
66 import System.Time ( ClockTime(..) )
71 #ifdef __GLASGOW_HASKELL__
73 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
77 A directory contains a series of entries, each of which is a named
78 reference to a file system object (file, directory etc.). Some
79 entries may be hidden, inaccessible, or have some administrative
80 function (e.g. `.' or `..' under POSIX
81 <http://www.opengroup.org/onlinepubs/007904975/toc.htm>), but in
82 this standard all such entries are considered to form part of the
83 directory contents. Entries in sub-directories are not, however,
84 considered to form part of the directory contents.
86 Each file system object is referenced by a /path/. There is
87 normally at least one absolute path to each file system object. In
88 some operating systems, it may also be possible to have paths which
89 are relative to the current directory.
92 -----------------------------------------------------------------------------
97 The 'Permissions' type is used to record whether certain operations are
98 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
99 get and set these permissions, respectively. Permissions apply both to
100 files and directories. For directories, the executable field will be
101 'False', and for files the searchable field will be 'False'. Note that
102 directories may be searchable without being readable, if permission has
103 been given to use them as part of a path, but not to examine the
106 Note that to change some, but not all permissions, a construct on the following lines must be used.
108 > makeReadable f = do
109 > p <- getPermissions f
110 > setPermissions f (p {readable = True})
117 executable, searchable :: Bool
118 } deriving (Eq, Ord, Read, Show)
120 getPermissions :: FilePath -> IO Permissions
121 getPermissions name = do
122 withCString name $ \s -> do
123 read <- c_access s r_OK
124 write <- c_access s w_OK
125 exec <- c_access s x_OK
126 withFileStatus name $ \st -> do
127 is_dir <- isDirectory st
130 readable = read == 0,
131 writable = write == 0,
132 executable = not is_dir && exec == 0,
133 searchable = is_dir && exec == 0
137 setPermissions :: FilePath -> Permissions -> IO ()
138 setPermissions name (Permissions r w e s) = do
140 read = if r then s_IRUSR else emptyCMode
141 write = if w then s_IWUSR else emptyCMode
142 exec = if e || s then s_IXUSR else emptyCMode
144 mode = read `unionCMode` (write `unionCMode` exec)
146 withCString name $ \s ->
147 throwErrnoIfMinus1_ "setPermissions" $ c_chmod s mode
149 -----------------------------------------------------------------------------
152 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
153 initially empty, or as near to empty as the operating system
156 The operation may fail with:
158 * 'isPermissionError' \/ 'PermissionDenied'
159 The process has insufficient privileges to perform the operation.
162 * 'isAlreadyExistsError' \/ 'AlreadyExists'
163 The operand refers to a directory that already exists.
167 A physical I\/O error has occurred.
171 The operand is not a valid directory name.
172 @[ENAMETOOLONG, ELOOP]@
175 There is no path to the directory.
178 * 'ResourceExhausted'
179 Insufficient resources (virtual memory, process file descriptors,
180 physical disk space, etc.) are available to perform the operation.
181 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
183 * 'InappropriateType'
184 The path refers to an existing non-directory object.
189 createDirectory :: FilePath -> IO ()
190 createDirectory path = do
191 withCString path $ \s -> do
192 throwErrnoIfMinus1Retry_ "createDirectory" $
195 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
196 implementation may specify additional constraints which must be
197 satisfied before a directory can be removed (e.g. the directory has to
198 be empty, or may not be in use by other processes). It is not legal
199 for an implementation to partially remove a directory unless the
200 entire directory is removed. A conformant implementation need not
201 support directory removal in all situations (e.g. removal of the root
204 The operation may fail with:
207 A physical I\/O error has occurred.
211 The operand is not a valid directory name.
212 [ENAMETOOLONG, ELOOP]
214 * 'isDoesNotExist' 'NoSuchThing'
215 The directory does not exist.
218 * 'isPermissionError' \/ 'PermissionDenied'
219 The process has insufficient privileges to perform the operation.
220 @[EROFS, EACCES, EPERM]@
222 * 'UnsatisfiedConstraints'
223 Implementation-dependent constraints are not satisfied.
224 @[EBUSY, ENOTEMPTY, EEXIST]@
226 * 'UnsupportedOperation'
227 The implementation does not support removal in this situation.
230 * 'InappropriateType'
231 The operand refers to an existing non-directory object.
236 removeDirectory :: FilePath -> IO ()
237 removeDirectory path = do
238 withCString path $ \s ->
239 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
241 {- |@'removefile' file@ removes the directory entry for an existing file
242 /file/, where /file/ is not itself a directory. The
243 implementation may specify additional constraints which must be
244 satisfied before a file can be removed (e.g. the file may not be in
245 use by other processes).
247 The operation may fail with:
250 A physical I\/O error has occurred.
254 The operand is not a valid file name.
255 @[ENAMETOOLONG, ELOOP]@
257 * 'isDoesNotExist' \/ 'NoSuchThing'
258 The file does not exist.
261 * 'isPermissionError' \/ 'PermissionDenied'
262 The process has insufficient privileges to perform the operation.
263 @[EROFS, EACCES, EPERM]@
265 * 'UnsatisfiedConstraints'
266 Implementation-dependent constraints are not satisfied.
269 * 'InappropriateType'
270 The operand refers to an existing directory.
275 removeFile :: FilePath -> IO ()
277 withCString path $ \s ->
278 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
280 {- |@'renameDirectory' old new@ changes the name of an existing
281 directory from /old/ to /new/. If the /new/ directory
282 already exists, it is atomically replaced by the /old/ directory.
283 If the /new/ directory is neither the /old/ directory nor an
284 alias of the /old/ directory, it is removed as if by
285 'removeDirectory'. A conformant implementation need not support
286 renaming directories in all situations (e.g. renaming to an existing
287 directory, or across different physical devices), but the constraints
290 The operation may fail with:
293 A physical I\/O error has occurred.
297 Either operand is not a valid directory name.
298 @[ENAMETOOLONG, ELOOP]@
300 * 'isDoesNotExistError' \/ 'NoSuchThing'
301 The original directory does not exist, or there is no path to the target.
304 * 'isPermissionError' \/ 'PermissionDenied'
305 The process has insufficient privileges to perform the operation.
306 @[EROFS, EACCES, EPERM]@
308 * 'ResourceExhausted'
309 Insufficient resources are available to perform the operation.
310 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
312 * 'UnsatisfiedConstraints'
313 Implementation-dependent constraints are not satisfied.
314 @[EBUSY, ENOTEMPTY, EEXIST]@
316 * 'UnsupportedOperation'
317 The implementation does not support renaming in this situation.
320 * 'InappropriateType'
321 Either path refers to an existing non-directory object.
326 renameDirectory :: FilePath -> FilePath -> IO ()
327 renameDirectory opath npath =
328 withFileStatus opath $ \st -> do
329 is_dir <- isDirectory st
331 then ioException (IOError Nothing InappropriateType "renameDirectory"
332 ("not a directory") (Just opath))
335 withCString opath $ \s1 ->
336 withCString npath $ \s2 ->
337 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
339 {- |@'renameFile' old new@ changes the name of an existing file system
340 object from /old/ to /new/. If the /new/ object already
341 exists, it is atomically replaced by the /old/ object. Neither
342 path may refer to an existing directory. A conformant implementation
343 need not support renaming files in all situations (e.g. renaming
344 across different physical devices), but the constraints must be
347 The operation may fail with:
350 A physical I\/O error has occurred.
354 Either operand is not a valid file name.
355 @[ENAMETOOLONG, ELOOP]@
357 * 'isDoesNotExistError' \/ 'NoSuchThing'
358 The original file does not exist, or there is no path to the target.
361 * 'isPermissionError' \/ 'PermissionDenied'
362 The process has insufficient privileges to perform the operation.
363 @[EROFS, EACCES, EPERM]@
365 * 'ResourceExhausted'
366 Insufficient resources are available to perform the operation.
367 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
369 * 'UnsatisfiedConstraints'
370 Implementation-dependent constraints are not satisfied.
373 * 'UnsupportedOperation'
374 The implementation does not support renaming in this situation.
377 * 'InappropriateType'
378 Either path refers to an existing directory.
379 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
383 renameFile :: FilePath -> FilePath -> IO ()
384 renameFile opath npath =
385 withFileOrSymlinkStatus opath $ \st -> do
386 is_dir <- isDirectory st
388 then ioException (IOError Nothing InappropriateType "renameFile"
389 "is a directory" (Just opath))
392 withCString opath $ \s1 ->
393 withCString npath $ \s2 ->
394 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
396 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
399 The operation may fail with:
402 A physical I\/O error has occurred.
406 The operand is not a valid directory name.
407 @[ENAMETOOLONG, ELOOP]@
409 * 'isDoesNotExistError' \/ 'NoSuchThing'
410 The directory does not exist.
413 * 'isPermissionError' \/ 'PermissionDenied'
414 The process has insufficient privileges to perform the operation.
417 * 'ResourceExhausted'
418 Insufficient resources are available to perform the operation.
421 * 'InappropriateType'
422 The path refers to an existing non-directory object.
427 getDirectoryContents :: FilePath -> IO [FilePath]
428 getDirectoryContents path = do
429 alloca $ \ ptr_dEnt ->
431 (withCString path $ \s ->
432 throwErrnoIfNullRetry desc (c_opendir s))
433 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
434 (\p -> loop ptr_dEnt p)
436 desc = "getDirectoryContents"
438 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
439 loop ptr_dEnt dir = do
441 r <- readdir dir ptr_dEnt
444 dEnt <- peek ptr_dEnt
448 entry <- (d_name dEnt >>= peekCString)
450 entries <- loop ptr_dEnt dir
451 return (entry:entries)
452 else do errno <- getErrno
453 if (errno == eINTR) then loop ptr_dEnt dir else do
454 let (Errno eo) = errno
455 if (eo == end_of_dir)
461 {- |If the operating system has a notion of current directories,
462 'getCurrentDirectory' returns an absolute path to the
463 current directory of the calling process.
465 The operation may fail with:
468 A physical I\/O error has occurred.
471 * 'isDoesNotExistError' \/ 'NoSuchThing'
472 There is no path referring to the current directory.
473 @[EPERM, ENOENT, ESTALE...]@
475 * 'isPermissionError' \/ 'PermissionDenied'
476 The process has insufficient privileges to perform the operation.
479 * 'ResourceExhausted'
480 Insufficient resources are available to perform the operation.
482 * 'UnsupportedOperation'
483 The operating system has no notion of current directory.
487 getCurrentDirectory :: IO FilePath
488 getCurrentDirectory = do
489 p <- mallocBytes path_max
491 where go p bytes = do
492 p' <- c_getcwd p (fromIntegral bytes)
494 then do s <- peekCString p'
497 else do errno <- getErrno
499 then do let bytes' = bytes * 2
500 p' <- reallocBytes p bytes'
502 else throwErrno "getCurrentDirectory"
504 {- |If the operating system has a notion of current directories,
505 @'setCurrentDirectory' dir@ changes the current
506 directory of the calling process to /dir/.
508 The operation may fail with:
511 A physical I\/O error has occurred.
515 The operand is not a valid directory name.
516 @[ENAMETOOLONG, ELOOP]@
518 * 'isDoesNotExistError' \/ 'NoSuchThing'
519 The directory does not exist.
522 * 'isPermissionError' \/ 'PermissionDenied'
523 The process has insufficient privileges to perform the operation.
526 * 'UnsupportedOperation'
527 The operating system has no notion of current directory, or the
528 current directory cannot be dynamically changed.
530 * 'InappropriateType'
531 The path refers to an existing non-directory object.
536 setCurrentDirectory :: FilePath -> IO ()
537 setCurrentDirectory path = do
538 withCString path $ \s ->
539 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
540 -- ToDo: add path to error
542 {- |To clarify, 'doesDirectoryExist' returns 'True' if a file system object
543 exist, and it's a directory. 'doesFileExist' returns 'True' if the file
544 system object exist, but it's not a directory (i.e., for every other
545 file system object that is not a directory.)
548 doesDirectoryExist :: FilePath -> IO Bool
549 doesDirectoryExist name =
551 (withFileStatus name $ \st -> isDirectory st)
552 (\ _ -> return False)
554 doesFileExist :: FilePath -> IO Bool
555 doesFileExist name = do
557 (withFileStatus name $ \st -> do b <- isDirectory st; return (not b))
558 (\ _ -> return False)
560 getModificationTime :: FilePath -> IO ClockTime
561 getModificationTime name =
562 withFileStatus name $ \ st ->
565 withFileStatus :: FilePath -> (Ptr CStat -> IO a) -> IO a
566 withFileStatus name f = do
567 allocaBytes sizeof_stat $ \p ->
568 withCString (fileNameEndClean name) $ \s -> do
569 throwErrnoIfMinus1Retry_ "withFileStatus" (c_stat s p)
572 withFileOrSymlinkStatus :: FilePath -> (Ptr CStat -> IO a) -> IO a
573 withFileOrSymlinkStatus name f = do
574 allocaBytes sizeof_stat $ \p ->
575 withCString name $ \s -> do
576 throwErrnoIfMinus1Retry_ "withFileOrSymlinkStatus" (lstat s p)
579 modificationTime :: Ptr CStat -> IO ClockTime
580 modificationTime stat = do
581 mtime <- st_mtime stat
582 return (TOD (toInteger (mtime :: CTime)) 0)
584 isDirectory :: Ptr CStat -> IO Bool
585 isDirectory stat = do
587 return (s_isdir mode)
589 fileNameEndClean :: String -> String
590 fileNameEndClean name =
591 if i >= 0 && (ec == '\\' || ec == '/') then
592 fileNameEndClean (take i name)
596 i = (length name) - 1
602 unionCMode :: CMode -> CMode -> CMode
606 foreign import ccall unsafe "__hscore_path_max"
609 foreign import ccall unsafe "__hscore_readdir"
610 readdir :: Ptr CDir -> Ptr (Ptr CDirent) -> IO CInt
612 foreign import ccall unsafe "__hscore_free_dirent"
613 freeDirEnt :: Ptr CDirent -> IO ()
615 foreign import ccall unsafe "__hscore_end_of_dir"
618 foreign import ccall unsafe "__hscore_d_name"
619 d_name :: Ptr CDirent -> IO CString
621 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
622 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
623 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
625 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
626 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
627 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode