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
9 -- Portability : portable
11 -- System-independent interface to directory manipulation.
13 -----------------------------------------------------------------------------
15 module System.Directory
19 -- * Actions on directories
20 createDirectory -- :: FilePath -> IO ()
21 , removeDirectory -- :: FilePath -> IO ()
22 , renameDirectory -- :: FilePath -> FilePath -> IO ()
24 , getDirectoryContents -- :: FilePath -> IO [FilePath]
25 , getCurrentDirectory -- :: IO FilePath
26 , setCurrentDirectory -- :: FilePath -> IO ()
29 , removeFile -- :: FilePath -> IO ()
30 , renameFile -- :: FilePath -> FilePath -> IO ()
31 #ifdef __GLASGOW_HASKELL__
32 , copyFile -- :: FilePath -> FilePath -> IO ()
36 , doesFileExist -- :: FilePath -> IO Bool
37 , doesDirectoryExist -- :: FilePath -> IO Bool
45 readable, -- :: Permissions -> Bool
46 writable, -- :: Permissions -> Bool
47 executable, -- :: Permissions -> Bool
48 searchable -- :: Permissions -> Bool
51 , getPermissions -- :: FilePath -> IO Permissions
52 , setPermissions -- :: FilePath -> Permissions -> IO ()
56 , getModificationTime -- :: FilePath -> IO ClockTime
61 #elif defined(__HUGS__)
67 import Control.Exception ( bracket )
68 import Control.Monad ( when )
69 import System.Posix.Types
70 import System.Posix.Internals
71 import System.Time ( ClockTime(..) )
73 import System.IO.Error
77 #ifdef __GLASGOW_HASKELL__
78 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
82 A directory contains a series of entries, each of which is a named
83 reference to a file system object (file, directory etc.). Some
84 entries may be hidden, inaccessible, or have some administrative
85 function (e.g. `.' or `..' under POSIX
86 <http://www.opengroup.org/onlinepubs/007904975/toc.htm>), but in
87 this standard all such entries are considered to form part of the
88 directory contents. Entries in sub-directories are not, however,
89 considered to form part of the directory contents.
91 Each file system object is referenced by a /path/. There is
92 normally at least one absolute path to each file system object. In
93 some operating systems, it may also be possible to have paths which
94 are relative to the current directory.
97 -----------------------------------------------------------------------------
102 The 'Permissions' type is used to record whether certain operations are
103 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
104 get and set these permissions, respectively. Permissions apply both to
105 files and directories. For directories, the executable field will be
106 'False', and for files the searchable field will be 'False'. Note that
107 directories may be searchable without being readable, if permission has
108 been given to use them as part of a path, but not to examine the
111 Note that to change some, but not all permissions, a construct on the following lines must be used.
113 > makeReadable f = do
114 > p <- getPermissions f
115 > setPermissions f (p {readable = True})
122 executable, searchable :: Bool
123 } deriving (Eq, Ord, Read, Show)
125 {- |The 'getPermissions' operation returns the
126 permissions for the file or directory.
128 The operation may fail with:
130 * 'isPermissionError' if the user is not permitted to access
133 * 'isDoesNotExistError' if the file or directory does not exist.
137 getPermissions :: FilePath -> IO Permissions
138 getPermissions name = do
139 withCString name $ \s -> do
140 read <- c_access s r_OK
141 write <- c_access s w_OK
142 exec <- c_access s x_OK
143 withFileStatus "getPermissions" name $ \st -> do
144 is_dir <- isDirectory st
147 readable = read == 0,
148 writable = write == 0,
149 executable = not is_dir && exec == 0,
150 searchable = is_dir && exec == 0
154 {- |The 'setPermissions' operation sets the
155 permissions for the file or directory.
157 The operation may fail with:
159 * 'isPermissionError' if the user is not permitted to set
162 * 'isDoesNotExistError' if the file or directory does not exist.
166 setPermissions :: FilePath -> Permissions -> IO ()
167 setPermissions name (Permissions r w e s) = do
168 allocaBytes sizeof_stat $ \ p_stat -> do
169 withCString name $ \p_name -> do
170 throwErrnoIfMinus1_ "setPermissions" $ do
172 mode <- st_mode p_stat
173 let mode1 = modifyBit r mode s_IRUSR
174 let mode2 = modifyBit w mode1 s_IWUSR
175 let mode3 = modifyBit (e || s) mode2 s_IXUSR
179 modifyBit :: Bool -> CMode -> CMode -> CMode
180 modifyBit False m b = m .&. (complement b)
181 modifyBit True m b = m .|. b
183 -----------------------------------------------------------------------------
186 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
187 initially empty, or as near to empty as the operating system
190 The operation may fail with:
192 * 'isPermissionError' \/ 'PermissionDenied'
193 The process has insufficient privileges to perform the operation.
196 * 'isAlreadyExistsError' \/ 'AlreadyExists'
197 The operand refers to a directory that already exists.
201 A physical I\/O error has occurred.
205 The operand is not a valid directory name.
206 @[ENAMETOOLONG, ELOOP]@
209 There is no path to the directory.
212 * 'ResourceExhausted'
213 Insufficient resources (virtual memory, process file descriptors,
214 physical disk space, etc.) are available to perform the operation.
215 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
217 * 'InappropriateType'
218 The path refers to an existing non-directory object.
223 createDirectory :: FilePath -> IO ()
224 createDirectory path = do
225 withCString path $ \s -> do
226 throwErrnoIfMinus1Retry_ "createDirectory" $
229 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
230 implementation may specify additional constraints which must be
231 satisfied before a directory can be removed (e.g. the directory has to
232 be empty, or may not be in use by other processes). It is not legal
233 for an implementation to partially remove a directory unless the
234 entire directory is removed. A conformant implementation need not
235 support directory removal in all situations (e.g. removal of the root
238 The operation may fail with:
241 A physical I\/O error has occurred.
245 The operand is not a valid directory name.
246 [ENAMETOOLONG, ELOOP]
248 * 'isDoesNotExistError' \/ 'NoSuchThing'
249 The directory does not exist.
252 * 'isPermissionError' \/ 'PermissionDenied'
253 The process has insufficient privileges to perform the operation.
254 @[EROFS, EACCES, EPERM]@
256 * 'UnsatisfiedConstraints'
257 Implementation-dependent constraints are not satisfied.
258 @[EBUSY, ENOTEMPTY, EEXIST]@
260 * 'UnsupportedOperation'
261 The implementation does not support removal in this situation.
264 * 'InappropriateType'
265 The operand refers to an existing non-directory object.
270 removeDirectory :: FilePath -> IO ()
271 removeDirectory path = do
272 modifyIOError (`ioeSetFileName` path) $
273 withCString path $ \s ->
274 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
276 {- |'removeFile' /file/ removes the directory entry for an existing file
277 /file/, where /file/ is not itself a directory. The
278 implementation may specify additional constraints which must be
279 satisfied before a file can be removed (e.g. the file may not be in
280 use by other processes).
282 The operation may fail with:
285 A physical I\/O error has occurred.
289 The operand is not a valid file name.
290 @[ENAMETOOLONG, ELOOP]@
292 * 'isDoesNotExistError' \/ 'NoSuchThing'
293 The file does not exist.
296 * 'isPermissionError' \/ 'PermissionDenied'
297 The process has insufficient privileges to perform the operation.
298 @[EROFS, EACCES, EPERM]@
300 * 'UnsatisfiedConstraints'
301 Implementation-dependent constraints are not satisfied.
304 * 'InappropriateType'
305 The operand refers to an existing directory.
310 removeFile :: FilePath -> IO ()
312 modifyIOError (`ioeSetFileName` path) $
313 withCString path $ \s ->
314 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
316 {- |@'renameDirectory' old new@ changes the name of an existing
317 directory from /old/ to /new/. If the /new/ directory
318 already exists, it is atomically replaced by the /old/ directory.
319 If the /new/ directory is neither the /old/ directory nor an
320 alias of the /old/ directory, it is removed as if by
321 'removeDirectory'. A conformant implementation need not support
322 renaming directories in all situations (e.g. renaming to an existing
323 directory, or across different physical devices), but the constraints
326 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
329 The operation may fail with:
332 A physical I\/O error has occurred.
336 Either operand is not a valid directory name.
337 @[ENAMETOOLONG, ELOOP]@
339 * 'isDoesNotExistError' \/ 'NoSuchThing'
340 The original directory does not exist, or there is no path to the target.
343 * 'isPermissionError' \/ 'PermissionDenied'
344 The process has insufficient privileges to perform the operation.
345 @[EROFS, EACCES, EPERM]@
347 * 'ResourceExhausted'
348 Insufficient resources are available to perform the operation.
349 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
351 * 'UnsatisfiedConstraints'
352 Implementation-dependent constraints are not satisfied.
353 @[EBUSY, ENOTEMPTY, EEXIST]@
355 * 'UnsupportedOperation'
356 The implementation does not support renaming in this situation.
359 * 'InappropriateType'
360 Either path refers to an existing non-directory object.
365 renameDirectory :: FilePath -> FilePath -> IO ()
366 renameDirectory opath npath =
367 withFileStatus "renameDirectory" opath $ \st -> do
368 is_dir <- isDirectory st
370 then ioException (IOError Nothing InappropriateType "renameDirectory"
371 ("not a directory") (Just opath))
374 withCString opath $ \s1 ->
375 withCString npath $ \s2 ->
376 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
378 {- |@'renameFile' old new@ changes the name of an existing file system
379 object from /old/ to /new/. If the /new/ object already
380 exists, it is atomically replaced by the /old/ object. Neither
381 path may refer to an existing directory. A conformant implementation
382 need not support renaming files in all situations (e.g. renaming
383 across different physical devices), but the constraints must be
386 The operation may fail with:
389 A physical I\/O error has occurred.
393 Either operand is not a valid file name.
394 @[ENAMETOOLONG, ELOOP]@
396 * 'isDoesNotExistError' \/ 'NoSuchThing'
397 The original file does not exist, or there is no path to the target.
400 * 'isPermissionError' \/ 'PermissionDenied'
401 The process has insufficient privileges to perform the operation.
402 @[EROFS, EACCES, EPERM]@
404 * 'ResourceExhausted'
405 Insufficient resources are available to perform the operation.
406 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
408 * 'UnsatisfiedConstraints'
409 Implementation-dependent constraints are not satisfied.
412 * 'UnsupportedOperation'
413 The implementation does not support renaming in this situation.
416 * 'InappropriateType'
417 Either path refers to an existing directory.
418 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
422 renameFile :: FilePath -> FilePath -> IO ()
423 renameFile opath npath =
424 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
425 is_dir <- isDirectory st
427 then ioException (IOError Nothing InappropriateType "renameFile"
428 "is a directory" (Just opath))
431 withCString opath $ \s1 ->
432 withCString npath $ \s2 ->
433 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
435 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
436 If the /new/ file already exists, it is atomically replaced by the /old/ file.
437 Neither path may refer to an existing directory.
439 copyFile :: FilePath -> FilePath -> IO ()
440 copyFile fromFPath toFPath =
441 (bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
442 bracket (openBinaryFile toFPath WriteMode) hClose $ \hTo ->
443 allocaBytes bufferSize $ \buffer ->
444 copyContents hFrom hTo buffer) `catch` (ioError . changeFunName)
448 changeFunName (IOError h iot fun str mb_fp) = IOError h iot "copyFile" str mb_fp
450 copyContents hFrom hTo buffer = do
451 count <- hGetBuf hFrom buffer bufferSize
452 when (count > 0) $ do
453 hPutBuf hTo buffer count
454 copyContents hFrom hTo buffer
457 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
460 The operation may fail with:
463 A physical I\/O error has occurred.
467 The operand is not a valid directory name.
468 @[ENAMETOOLONG, ELOOP]@
470 * 'isDoesNotExistError' \/ 'NoSuchThing'
471 The directory does not exist.
474 * 'isPermissionError' \/ 'PermissionDenied'
475 The process has insufficient privileges to perform the operation.
478 * 'ResourceExhausted'
479 Insufficient resources are available to perform the operation.
482 * 'InappropriateType'
483 The path refers to an existing non-directory object.
488 getDirectoryContents :: FilePath -> IO [FilePath]
489 getDirectoryContents path = do
490 modifyIOError (`ioeSetFileName` path) $
491 alloca $ \ ptr_dEnt ->
493 (withCString path $ \s ->
494 throwErrnoIfNullRetry desc (c_opendir s))
495 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
496 (\p -> loop ptr_dEnt p)
498 desc = "getDirectoryContents"
500 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
501 loop ptr_dEnt dir = do
503 r <- readdir dir ptr_dEnt
506 dEnt <- peek ptr_dEnt
510 entry <- (d_name dEnt >>= peekCString)
512 entries <- loop ptr_dEnt dir
513 return (entry:entries)
514 else do errno <- getErrno
515 if (errno == eINTR) then loop ptr_dEnt dir else do
516 let (Errno eo) = errno
517 if (eo == end_of_dir)
523 {- |If the operating system has a notion of current directories,
524 'getCurrentDirectory' returns an absolute path to the
525 current directory of the calling process.
527 The operation may fail with:
530 A physical I\/O error has occurred.
533 * 'isDoesNotExistError' \/ 'NoSuchThing'
534 There is no path referring to the current directory.
535 @[EPERM, ENOENT, ESTALE...]@
537 * 'isPermissionError' \/ 'PermissionDenied'
538 The process has insufficient privileges to perform the operation.
541 * 'ResourceExhausted'
542 Insufficient resources are available to perform the operation.
544 * 'UnsupportedOperation'
545 The operating system has no notion of current directory.
549 getCurrentDirectory :: IO FilePath
550 getCurrentDirectory = do
551 p <- mallocBytes long_path_size
553 where go p bytes = do
554 p' <- c_getcwd p (fromIntegral bytes)
556 then do s <- peekCString p'
559 else do errno <- getErrno
561 then do let bytes' = bytes * 2
562 p' <- reallocBytes p bytes'
564 else throwErrno "getCurrentDirectory"
566 {- |If the operating system has a notion of current directories,
567 @'setCurrentDirectory' dir@ changes the current
568 directory of the calling process to /dir/.
570 The operation may fail with:
573 A physical I\/O error has occurred.
577 The operand is not a valid directory name.
578 @[ENAMETOOLONG, ELOOP]@
580 * 'isDoesNotExistError' \/ 'NoSuchThing'
581 The directory does not exist.
584 * 'isPermissionError' \/ 'PermissionDenied'
585 The process has insufficient privileges to perform the operation.
588 * 'UnsupportedOperation'
589 The operating system has no notion of current directory, or the
590 current directory cannot be dynamically changed.
592 * 'InappropriateType'
593 The path refers to an existing non-directory object.
598 setCurrentDirectory :: FilePath -> IO ()
599 setCurrentDirectory path = do
600 modifyIOError (`ioeSetFileName` path) $
601 withCString path $ \s ->
602 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
603 -- ToDo: add path to error
605 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
606 exists and is a directory, and 'False' otherwise.
609 doesDirectoryExist :: FilePath -> IO Bool
610 doesDirectoryExist name =
612 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
613 (\ _ -> return False)
615 {- |The operation 'doesFileExist' returns 'True'
616 if the argument file exists and is not a directory, and 'False' otherwise.
619 doesFileExist :: FilePath -> IO Bool
620 doesFileExist name = do
622 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
623 (\ _ -> return False)
625 {- |The 'getModificationTime' operation returns the
626 clock time at which the file or directory was last modified.
628 The operation may fail with:
630 * 'isPermissionError' if the user is not permitted to access
631 the modification time; or
633 * 'isDoesNotExistError' if the file or directory does not exist.
637 getModificationTime :: FilePath -> IO ClockTime
638 getModificationTime name =
639 withFileStatus "getModificationTime" name $ \ st ->
642 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
643 withFileStatus loc name f = do
644 modifyIOError (`ioeSetFileName` name) $
645 allocaBytes sizeof_stat $ \p ->
646 withCString (fileNameEndClean name) $ \s -> do
647 throwErrnoIfMinus1Retry_ loc (c_stat s p)
650 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
651 withFileOrSymlinkStatus loc name f = do
652 modifyIOError (`ioeSetFileName` name) $
653 allocaBytes sizeof_stat $ \p ->
654 withCString name $ \s -> do
655 throwErrnoIfMinus1Retry_ loc (lstat s p)
658 modificationTime :: Ptr CStat -> IO ClockTime
659 modificationTime stat = do
660 mtime <- st_mtime stat
661 let realToInteger = round . realToFrac :: Real a => a -> Integer
662 return (TOD (realToInteger (mtime :: CTime)) 0)
664 isDirectory :: Ptr CStat -> IO Bool
665 isDirectory stat = do
667 return (s_isdir mode)
669 fileNameEndClean :: String -> String
670 fileNameEndClean name =
671 if i > 0 && (ec == '\\' || ec == '/') then
672 fileNameEndClean (take i name)
676 i = (length name) - 1
679 foreign import ccall unsafe "__hscore_long_path_size"
680 long_path_size :: Int
682 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
683 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
684 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
686 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
687 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
688 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode