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 Control.Exception ( bracket )
61 import System.Posix.Types
62 import System.Time ( ClockTime(..) )
67 #ifdef __GLASGOW_HASKELL__
69 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
73 A directory contains a series of entries, each of which is a named
74 reference to a file system object (file, directory etc.). Some
75 entries may be hidden, inaccessible, or have some administrative
76 function (e.g. `.' or `..' under POSIX
77 <http://www.opengroup.org/onlinepubs/007904975/toc.htm>), but in
78 this standard all such entries are considered to form part of the
79 directory contents. Entries in sub-directories are not, however,
80 considered to form part of the directory contents.
82 Each file system object is referenced by a /path/. There is
83 normally at least one absolute path to each file system object. In
84 some operating systems, it may also be possible to have paths which
85 are relative to the current directory.
88 -----------------------------------------------------------------------------
93 The 'Permissions' type is used to record whether certain operations are
94 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
95 get and set these permissions, respectively. Permissions apply both to
96 files and directories. For directories, the executable field will be
97 'False', and for files the searchable field will be 'False'. Note that
98 directories may be searchable without being readable, if permission has
99 been given to use them as part of a path, but not to examine the
102 Note that to change some, but not all permissions, a construct on the following lines must be used.
104 > makeReadable f = do
105 > p <- getPermissions f
106 > setPermissions f (p {readable = True})
113 executable, searchable :: Bool
114 } deriving (Eq, Ord, Read, Show)
116 getPermissions :: FilePath -> IO Permissions
117 getPermissions name = do
118 withCString name $ \s -> do
119 read <- c_access s r_OK
120 write <- c_access s w_OK
121 exec <- c_access s x_OK
122 withFileStatus name $ \st -> do
123 is_dir <- isDirectory st
126 readable = read == 0,
127 writable = write == 0,
128 executable = not is_dir && exec == 0,
129 searchable = is_dir && exec == 0
133 setPermissions :: FilePath -> Permissions -> IO ()
134 setPermissions name (Permissions r w e s) = do
136 read = if r then s_IRUSR else emptyCMode
137 write = if w then s_IWUSR else emptyCMode
138 exec = if e || s then s_IXUSR else emptyCMode
140 mode = read `unionCMode` (write `unionCMode` exec)
142 withCString name $ \s ->
143 throwErrnoIfMinus1_ "setPermissions" $ c_chmod s mode
145 -----------------------------------------------------------------------------
148 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
149 initially empty, or as near to empty as the operating system
152 The operation may fail with:
154 * 'isPermissionError' \/ 'PermissionDenied'
155 The process has insufficient privileges to perform the operation.
158 * 'isAlreadyExistsError' \/ 'AlreadyExists'
159 The operand refers to a directory that already exists.
163 A physical I\/O error has occurred.
167 The operand is not a valid directory name.
168 @[ENAMETOOLONG, ELOOP]@
171 There is no path to the directory.
174 * 'ResourceExhausted'
175 Insufficient resources (virtual memory, process file descriptors,
176 physical disk space, etc.) are available to perform the operation.
177 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
179 * 'InappropriateType'
180 The path refers to an existing non-directory object.
185 createDirectory :: FilePath -> IO ()
186 createDirectory path = do
187 withCString path $ \s -> do
188 throwErrnoIfMinus1Retry_ "createDirectory" $
191 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
192 implementation may specify additional constraints which must be
193 satisfied before a directory can be removed (e.g. the directory has to
194 be empty, or may not be in use by other processes). It is not legal
195 for an implementation to partially remove a directory unless the
196 entire directory is removed. A conformant implementation need not
197 support directory removal in all situations (e.g. removal of the root
200 The operation may fail with:
203 A physical I\/O error has occurred.
207 The operand is not a valid directory name.
208 [ENAMETOOLONG, ELOOP]
210 * 'isDoesNotExist' 'NoSuchThing'
211 The directory does not exist.
214 * 'isPermissionError' \/ 'PermissionDenied'
215 The process has insufficient privileges to perform the operation.
216 @[EROFS, EACCES, EPERM]@
218 * 'UnsatisfiedConstraints'
219 Implementation-dependent constraints are not satisfied.
220 @[EBUSY, ENOTEMPTY, EEXIST]@
222 * 'UnsupportedOperation'
223 The implementation does not support removal in this situation.
226 * 'InappropriateType'
227 The operand refers to an existing non-directory object.
232 removeDirectory :: FilePath -> IO ()
233 removeDirectory path = do
234 withCString path $ \s ->
235 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
237 {- |@'removefile' file@ removes the directory entry for an existing file
238 /file/, where /file/ is not itself a directory. The
239 implementation may specify additional constraints which must be
240 satisfied before a file can be removed (e.g. the file may not be in
241 use by other processes).
243 The operation may fail with:
246 A physical I\/O error has occurred.
250 The operand is not a valid file name.
251 @[ENAMETOOLONG, ELOOP]@
253 * 'isDoesNotExist' \/ 'NoSuchThing'
254 The file does not exist.
257 * 'isPermissionError' \/ 'PermissionDenied'
258 The process has insufficient privileges to perform the operation.
259 @[EROFS, EACCES, EPERM]@
261 * 'UnsatisfiedConstraints'
262 Implementation-dependent constraints are not satisfied.
265 * 'InappropriateType'
266 The operand refers to an existing directory.
271 removeFile :: FilePath -> IO ()
273 withCString path $ \s ->
274 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
276 {- |@'renameDirectory' old new@ changes the name of an existing
277 directory from /old/ to /new/. If the /new/ directory
278 already exists, it is atomically replaced by the /old/ directory.
279 If the /new/ directory is neither the /old/ directory nor an
280 alias of the /old/ directory, it is removed as if by
281 'removeDirectory'. A conformant implementation need not support
282 renaming directories in all situations (e.g. renaming to an existing
283 directory, or across different physical devices), but the constraints
286 The operation may fail with:
289 A physical I\/O error has occurred.
293 Either operand is not a valid directory name.
294 @[ENAMETOOLONG, ELOOP]@
296 * 'isDoesNotExistError' \/ 'NoSuchThing'
297 The original directory does not exist, or there is no path to the target.
300 * 'isPermissionError' \/ 'PermissionDenied'
301 The process has insufficient privileges to perform the operation.
302 @[EROFS, EACCES, EPERM]@
304 * 'ResourceExhausted'
305 Insufficient resources are available to perform the operation.
306 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
308 * 'UnsatisfiedConstraints'
309 Implementation-dependent constraints are not satisfied.
310 @[EBUSY, ENOTEMPTY, EEXIST]@
312 * 'UnsupportedOperation'
313 The implementation does not support renaming in this situation.
316 * 'InappropriateType'
317 Either path refers to an existing non-directory object.
322 renameDirectory :: FilePath -> FilePath -> IO ()
323 renameDirectory opath npath =
324 withFileStatus opath $ \st -> do
325 is_dir <- isDirectory st
327 then ioException (IOError Nothing InappropriateType "renameDirectory"
328 ("not a directory") (Just opath))
331 withCString opath $ \s1 ->
332 withCString npath $ \s2 ->
333 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
335 {- |@'renameFile' old new@ changes the name of an existing file system
336 object from /old/ to /new/. If the /new/ object already
337 exists, it is atomically replaced by the /old/ object. Neither
338 path may refer to an existing directory. A conformant implementation
339 need not support renaming files in all situations (e.g. renaming
340 across different physical devices), but the constraints must be
343 The operation may fail with:
346 A physical I\/O error has occurred.
350 Either operand is not a valid file name.
351 @[ENAMETOOLONG, ELOOP]@
353 * 'isDoesNotExistError' \/ 'NoSuchThing'
354 The original file does not exist, or there is no path to the target.
357 * 'isPermissionError' \/ 'PermissionDenied'
358 The process has insufficient privileges to perform the operation.
359 @[EROFS, EACCES, EPERM]@
361 * 'ResourceExhausted'
362 Insufficient resources are available to perform the operation.
363 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
365 * 'UnsatisfiedConstraints'
366 Implementation-dependent constraints are not satisfied.
369 * 'UnsupportedOperation'
370 The implementation does not support renaming in this situation.
373 * 'InappropriateType'
374 Either path refers to an existing directory.
375 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
379 renameFile :: FilePath -> FilePath -> IO ()
380 renameFile opath npath =
381 withFileOrSymlinkStatus opath $ \st -> do
382 is_dir <- isDirectory st
384 then ioException (IOError Nothing InappropriateType "renameFile"
385 "is a directory" (Just opath))
388 withCString opath $ \s1 ->
389 withCString npath $ \s2 ->
390 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
392 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
395 The operation may fail with:
398 A physical I\/O error has occurred.
402 The operand is not a valid directory name.
403 @[ENAMETOOLONG, ELOOP]@
405 * 'isDoesNotExistError' \/ 'NoSuchThing'
406 The directory does not exist.
409 * 'isPermissionError' \/ 'PermissionDenied'
410 The process has insufficient privileges to perform the operation.
413 * 'ResourceExhausted'
414 Insufficient resources are available to perform the operation.
417 * 'InappropriateType'
418 The path refers to an existing non-directory object.
423 getDirectoryContents :: FilePath -> IO [FilePath]
424 getDirectoryContents path = do
425 alloca $ \ ptr_dEnt ->
427 (withCString path $ \s ->
428 throwErrnoIfNullRetry desc (c_opendir s))
429 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
430 (\p -> loop ptr_dEnt p)
432 desc = "getDirectoryContents"
434 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
435 loop ptr_dEnt dir = do
437 r <- readdir dir ptr_dEnt
440 dEnt <- peek ptr_dEnt
444 entry <- (d_name dEnt >>= peekCString)
446 entries <- loop ptr_dEnt dir
447 return (entry:entries)
448 else do errno <- getErrno
449 if (errno == eINTR) then loop ptr_dEnt dir else do
450 let (Errno eo) = errno
451 if (eo == end_of_dir)
457 {- |If the operating system has a notion of current directories,
458 'getCurrentDirectory' returns an absolute path to the
459 current directory of the calling process.
461 The operation may fail with:
464 A physical I\/O error has occurred.
467 * 'isDoesNotExistError' \/ 'NoSuchThing'
468 There is no path referring to the current directory.
469 @[EPERM, ENOENT, ESTALE...]@
471 * 'isPermissionError' \/ 'PermissionDenied'
472 The process has insufficient privileges to perform the operation.
475 * 'ResourceExhausted'
476 Insufficient resources are available to perform the operation.
478 * 'UnsupportedOperation'
479 The operating system has no notion of current directory.
483 getCurrentDirectory :: IO FilePath
484 getCurrentDirectory = do
485 p <- mallocBytes path_max
487 where go p bytes = do
488 p' <- c_getcwd p (fromIntegral bytes)
490 then do s <- peekCString p'
493 else do errno <- getErrno
495 then do let bytes' = bytes * 2
496 p' <- reallocBytes p bytes'
498 else throwErrno "getCurrentDirectory"
500 {- |If the operating system has a notion of current directories,
501 @'setCurrentDirectory' dir@ changes the current
502 directory of the calling process to /dir/.
504 The operation may fail with:
507 A physical I\/O error has occurred.
511 The operand is not a valid directory name.
512 @[ENAMETOOLONG, ELOOP]@
514 * 'isDoesNotExistError' \/ 'NoSuchThing'
515 The directory does not exist.
518 * 'isPermissionError' \/ 'PermissionDenied'
519 The process has insufficient privileges to perform the operation.
522 * 'UnsupportedOperation'
523 The operating system has no notion of current directory, or the
524 current directory cannot be dynamically changed.
526 * 'InappropriateType'
527 The path refers to an existing non-directory object.
532 setCurrentDirectory :: FilePath -> IO ()
533 setCurrentDirectory path = do
534 withCString path $ \s ->
535 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
536 -- ToDo: add path to error
538 {- |To clarify, 'doesDirectoryExist' returns 'True' if a file system object
539 exist, and it's a directory. 'doesFileExist' returns 'True' if the file
540 system object exist, but it's not a directory (i.e., for every other
541 file system object that is not a directory.)
544 doesDirectoryExist :: FilePath -> IO Bool
545 doesDirectoryExist name =
547 (withFileStatus name $ \st -> isDirectory st)
548 (\ _ -> return False)
550 doesFileExist :: FilePath -> IO Bool
551 doesFileExist name = do
553 (withFileStatus name $ \st -> do b <- isDirectory st; return (not b))
554 (\ _ -> return False)
556 getModificationTime :: FilePath -> IO ClockTime
557 getModificationTime name =
558 withFileStatus name $ \ st ->
561 withFileStatus :: FilePath -> (Ptr CStat -> IO a) -> IO a
562 withFileStatus name f = do
563 allocaBytes sizeof_stat $ \p ->
564 withCString (fileNameEndClean name) $ \s -> do
565 throwErrnoIfMinus1Retry_ "withFileStatus" (c_stat s p)
568 withFileOrSymlinkStatus :: FilePath -> (Ptr CStat -> IO a) -> IO a
569 withFileOrSymlinkStatus name f = do
570 allocaBytes sizeof_stat $ \p ->
571 withCString name $ \s -> do
572 throwErrnoIfMinus1Retry_ "withFileOrSymlinkStatus" (lstat s p)
575 modificationTime :: Ptr CStat -> IO ClockTime
576 modificationTime stat = do
577 mtime <- st_mtime stat
578 return (TOD (toInteger (mtime :: CTime)) 0)
580 isDirectory :: Ptr CStat -> IO Bool
581 isDirectory stat = do
583 return (s_isdir mode)
585 fileNameEndClean :: String -> String
586 fileNameEndClean name =
587 if i >= 0 && (ec == '\\' || ec == '/') then
588 fileNameEndClean (take i name)
592 i = (length name) - 1
598 unionCMode :: CMode -> CMode -> CMode
602 foreign import ccall unsafe "__hscore_path_max"
605 foreign import ccall unsafe "__hscore_readdir"
606 readdir :: Ptr CDir -> Ptr (Ptr CDirent) -> IO CInt
608 foreign import ccall unsafe "__hscore_free_dirent"
609 freeDirEnt :: Ptr CDirent -> IO ()
611 foreign import ccall unsafe "__hscore_end_of_dir"
614 foreign import ccall unsafe "__hscore_d_name"
615 d_name :: Ptr CDirent -> IO CString
617 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
618 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
619 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
621 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
622 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
623 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode