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 , copyFile -- :: FilePath -> FilePath -> IO ()
34 , doesFileExist -- :: FilePath -> IO Bool
35 , doesDirectoryExist -- :: FilePath -> IO Bool
43 readable, -- :: Permissions -> Bool
44 writable, -- :: Permissions -> Bool
45 executable, -- :: Permissions -> Bool
46 searchable -- :: Permissions -> Bool
49 , getPermissions -- :: FilePath -> IO Permissions
50 , setPermissions -- :: FilePath -> Permissions -> IO ()
54 , getModificationTime -- :: FilePath -> IO ClockTime
59 #elif defined(__HUGS__)
65 import Control.Exception ( bracket )
66 import Control.Monad ( when )
67 import System.Posix.Types
68 import System.Posix.Internals
69 import System.Time ( ClockTime(..) )
71 import System.IO.Error
75 #ifdef __GLASGOW_HASKELL__
76 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
80 A directory contains a series of entries, each of which is a named
81 reference to a file system object (file, directory etc.). Some
82 entries may be hidden, inaccessible, or have some administrative
83 function (e.g. `.' or `..' under POSIX
84 <http://www.opengroup.org/onlinepubs/007904975/toc.htm>), but in
85 this standard all such entries are considered to form part of the
86 directory contents. Entries in sub-directories are not, however,
87 considered to form part of the directory contents.
89 Each file system object is referenced by a /path/. There is
90 normally at least one absolute path to each file system object. In
91 some operating systems, it may also be possible to have paths which
92 are relative to the current directory.
95 -----------------------------------------------------------------------------
100 The 'Permissions' type is used to record whether certain operations are
101 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
102 get and set these permissions, respectively. Permissions apply both to
103 files and directories. For directories, the executable field will be
104 'False', and for files the searchable field will be 'False'. Note that
105 directories may be searchable without being readable, if permission has
106 been given to use them as part of a path, but not to examine the
109 Note that to change some, but not all permissions, a construct on the following lines must be used.
111 > makeReadable f = do
112 > p <- getPermissions f
113 > setPermissions f (p {readable = True})
120 executable, searchable :: Bool
121 } deriving (Eq, Ord, Read, Show)
123 {- |The 'getPermissions' operation returns the
124 permissions for the file or directory.
126 The operation may fail with:
128 * 'isPermissionError' if the user is not permitted to access
131 * 'isDoesNotExistError' if the file or directory does not exist.
135 getPermissions :: FilePath -> IO Permissions
136 getPermissions name = do
137 withCString name $ \s -> do
138 read <- c_access s r_OK
139 write <- c_access s w_OK
140 exec <- c_access s x_OK
141 withFileStatus "getPermissions" name $ \st -> do
142 is_dir <- isDirectory st
145 readable = read == 0,
146 writable = write == 0,
147 executable = not is_dir && exec == 0,
148 searchable = is_dir && exec == 0
152 {- |The 'setPermissions' operation sets the
153 permissions for the file or directory.
155 The operation may fail with:
157 * 'isPermissionError' if the user is not permitted to set
160 * 'isDoesNotExistError' if the file or directory does not exist.
164 setPermissions :: FilePath -> Permissions -> IO ()
165 setPermissions name (Permissions r w e s) = do
166 allocaBytes sizeof_stat $ \ p_stat -> do
167 withCString name $ \p_name -> do
168 throwErrnoIfMinus1_ "setPermissions" $ do
170 mode <- st_mode p_stat
171 let mode1 = modifyBit r mode s_IRUSR
172 let mode2 = modifyBit w mode1 s_IWUSR
173 let mode3 = modifyBit (e || s) mode2 s_IXUSR
177 modifyBit :: Bool -> CMode -> CMode -> CMode
178 modifyBit False m b = m .&. (complement b)
179 modifyBit True m b = m .|. b
181 -----------------------------------------------------------------------------
184 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
185 initially empty, or as near to empty as the operating system
188 The operation may fail with:
190 * 'isPermissionError' \/ 'PermissionDenied'
191 The process has insufficient privileges to perform the operation.
194 * 'isAlreadyExistsError' \/ 'AlreadyExists'
195 The operand refers to a directory that already exists.
199 A physical I\/O error has occurred.
203 The operand is not a valid directory name.
204 @[ENAMETOOLONG, ELOOP]@
207 There is no path to the directory.
210 * 'ResourceExhausted'
211 Insufficient resources (virtual memory, process file descriptors,
212 physical disk space, etc.) are available to perform the operation.
213 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
215 * 'InappropriateType'
216 The path refers to an existing non-directory object.
221 createDirectory :: FilePath -> IO ()
222 createDirectory path = do
223 withCString path $ \s -> do
224 throwErrnoIfMinus1Retry_ "createDirectory" $
227 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
228 implementation may specify additional constraints which must be
229 satisfied before a directory can be removed (e.g. the directory has to
230 be empty, or may not be in use by other processes). It is not legal
231 for an implementation to partially remove a directory unless the
232 entire directory is removed. A conformant implementation need not
233 support directory removal in all situations (e.g. removal of the root
236 The operation may fail with:
239 A physical I\/O error has occurred.
243 The operand is not a valid directory name.
244 [ENAMETOOLONG, ELOOP]
246 * 'isDoesNotExistError' \/ 'NoSuchThing'
247 The directory does not exist.
250 * 'isPermissionError' \/ 'PermissionDenied'
251 The process has insufficient privileges to perform the operation.
252 @[EROFS, EACCES, EPERM]@
254 * 'UnsatisfiedConstraints'
255 Implementation-dependent constraints are not satisfied.
256 @[EBUSY, ENOTEMPTY, EEXIST]@
258 * 'UnsupportedOperation'
259 The implementation does not support removal in this situation.
262 * 'InappropriateType'
263 The operand refers to an existing non-directory object.
268 removeDirectory :: FilePath -> IO ()
269 removeDirectory path = do
270 modifyIOError (`ioeSetFileName` path) $
271 withCString path $ \s ->
272 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
274 {- |'removeFile' /file/ removes the directory entry for an existing file
275 /file/, where /file/ is not itself a directory. The
276 implementation may specify additional constraints which must be
277 satisfied before a file can be removed (e.g. the file may not be in
278 use by other processes).
280 The operation may fail with:
283 A physical I\/O error has occurred.
287 The operand is not a valid file name.
288 @[ENAMETOOLONG, ELOOP]@
290 * 'isDoesNotExistError' \/ 'NoSuchThing'
291 The file does not exist.
294 * 'isPermissionError' \/ 'PermissionDenied'
295 The process has insufficient privileges to perform the operation.
296 @[EROFS, EACCES, EPERM]@
298 * 'UnsatisfiedConstraints'
299 Implementation-dependent constraints are not satisfied.
302 * 'InappropriateType'
303 The operand refers to an existing directory.
308 removeFile :: FilePath -> IO ()
310 modifyIOError (`ioeSetFileName` path) $
311 withCString path $ \s ->
312 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
314 {- |@'renameDirectory' old new@ changes the name of an existing
315 directory from /old/ to /new/. If the /new/ directory
316 already exists, it is atomically replaced by the /old/ directory.
317 If the /new/ directory is neither the /old/ directory nor an
318 alias of the /old/ directory, it is removed as if by
319 'removeDirectory'. A conformant implementation need not support
320 renaming directories in all situations (e.g. renaming to an existing
321 directory, or across different physical devices), but the constraints
324 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
327 The operation may fail with:
330 A physical I\/O error has occurred.
334 Either operand is not a valid directory name.
335 @[ENAMETOOLONG, ELOOP]@
337 * 'isDoesNotExistError' \/ 'NoSuchThing'
338 The original directory does not exist, or there is no path to the target.
341 * 'isPermissionError' \/ 'PermissionDenied'
342 The process has insufficient privileges to perform the operation.
343 @[EROFS, EACCES, EPERM]@
345 * 'ResourceExhausted'
346 Insufficient resources are available to perform the operation.
347 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
349 * 'UnsatisfiedConstraints'
350 Implementation-dependent constraints are not satisfied.
351 @[EBUSY, ENOTEMPTY, EEXIST]@
353 * 'UnsupportedOperation'
354 The implementation does not support renaming in this situation.
357 * 'InappropriateType'
358 Either path refers to an existing non-directory object.
363 renameDirectory :: FilePath -> FilePath -> IO ()
364 renameDirectory opath npath =
365 withFileStatus "renameDirectory" opath $ \st -> do
366 is_dir <- isDirectory st
368 then ioException (IOError Nothing InappropriateType "renameDirectory"
369 ("not a directory") (Just opath))
372 withCString opath $ \s1 ->
373 withCString npath $ \s2 ->
374 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
376 {- |@'renameFile' old new@ changes the name of an existing file system
377 object from /old/ to /new/. If the /new/ object already
378 exists, it is atomically replaced by the /old/ object. Neither
379 path may refer to an existing directory. A conformant implementation
380 need not support renaming files in all situations (e.g. renaming
381 across different physical devices), but the constraints must be
384 The operation may fail with:
387 A physical I\/O error has occurred.
391 Either operand is not a valid file name.
392 @[ENAMETOOLONG, ELOOP]@
394 * 'isDoesNotExistError' \/ 'NoSuchThing'
395 The original file does not exist, or there is no path to the target.
398 * 'isPermissionError' \/ 'PermissionDenied'
399 The process has insufficient privileges to perform the operation.
400 @[EROFS, EACCES, EPERM]@
402 * 'ResourceExhausted'
403 Insufficient resources are available to perform the operation.
404 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
406 * 'UnsatisfiedConstraints'
407 Implementation-dependent constraints are not satisfied.
410 * 'UnsupportedOperation'
411 The implementation does not support renaming in this situation.
414 * 'InappropriateType'
415 Either path refers to an existing directory.
416 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
420 renameFile :: FilePath -> FilePath -> IO ()
421 renameFile opath npath =
422 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
423 is_dir <- isDirectory st
425 then ioException (IOError Nothing InappropriateType "renameFile"
426 "is a directory" (Just opath))
429 withCString opath $ \s1 ->
430 withCString npath $ \s2 ->
431 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
433 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
434 If the /new/ file already exists, it is atomically replaced by the /old/ file.
435 Neither path may refer to an existing directory.
437 copyFile :: FilePath -> FilePath -> IO ()
438 copyFile fromFPath toFPath = handle (changeFunName) $
439 (bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
440 bracket (openBinaryFile toFPath WriteMode) hClose $ \hTo ->
441 allocaBytes bufferSize $ \buffer ->
442 copyContents hFrom hTo buffer) `catch` (ioError . changeFunName)
446 changeFunName (IOError h iot fun str mb_fp) = IOError h iot "copyFile" str mb_fp
449 copyContents hFrom hTo buffer = do
450 count <- hGetBuf hFrom buffer bufferSize
451 when (count > 0) $ do
452 hPutBuf hTo buffer count
453 copyContents hFrom hTo buffer
456 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
459 The operation may fail with:
462 A physical I\/O error has occurred.
466 The operand is not a valid directory name.
467 @[ENAMETOOLONG, ELOOP]@
469 * 'isDoesNotExistError' \/ 'NoSuchThing'
470 The directory does not exist.
473 * 'isPermissionError' \/ 'PermissionDenied'
474 The process has insufficient privileges to perform the operation.
477 * 'ResourceExhausted'
478 Insufficient resources are available to perform the operation.
481 * 'InappropriateType'
482 The path refers to an existing non-directory object.
487 getDirectoryContents :: FilePath -> IO [FilePath]
488 getDirectoryContents path = do
489 modifyIOError (`ioeSetFileName` path) $
490 alloca $ \ ptr_dEnt ->
492 (withCString path $ \s ->
493 throwErrnoIfNullRetry desc (c_opendir s))
494 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
495 (\p -> loop ptr_dEnt p)
497 desc = "getDirectoryContents"
499 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
500 loop ptr_dEnt dir = do
502 r <- readdir dir ptr_dEnt
505 dEnt <- peek ptr_dEnt
509 entry <- (d_name dEnt >>= peekCString)
511 entries <- loop ptr_dEnt dir
512 return (entry:entries)
513 else do errno <- getErrno
514 if (errno == eINTR) then loop ptr_dEnt dir else do
515 let (Errno eo) = errno
516 if (eo == end_of_dir)
522 {- |If the operating system has a notion of current directories,
523 'getCurrentDirectory' returns an absolute path to the
524 current directory of the calling process.
526 The operation may fail with:
529 A physical I\/O error has occurred.
532 * 'isDoesNotExistError' \/ 'NoSuchThing'
533 There is no path referring to the current directory.
534 @[EPERM, ENOENT, ESTALE...]@
536 * 'isPermissionError' \/ 'PermissionDenied'
537 The process has insufficient privileges to perform the operation.
540 * 'ResourceExhausted'
541 Insufficient resources are available to perform the operation.
543 * 'UnsupportedOperation'
544 The operating system has no notion of current directory.
548 getCurrentDirectory :: IO FilePath
549 getCurrentDirectory = do
550 p <- mallocBytes long_path_size
552 where go p bytes = do
553 p' <- c_getcwd p (fromIntegral bytes)
555 then do s <- peekCString p'
558 else do errno <- getErrno
560 then do let bytes' = bytes * 2
561 p' <- reallocBytes p bytes'
563 else throwErrno "getCurrentDirectory"
565 {- |If the operating system has a notion of current directories,
566 @'setCurrentDirectory' dir@ changes the current
567 directory of the calling process to /dir/.
569 The operation may fail with:
572 A physical I\/O error has occurred.
576 The operand is not a valid directory name.
577 @[ENAMETOOLONG, ELOOP]@
579 * 'isDoesNotExistError' \/ 'NoSuchThing'
580 The directory does not exist.
583 * 'isPermissionError' \/ 'PermissionDenied'
584 The process has insufficient privileges to perform the operation.
587 * 'UnsupportedOperation'
588 The operating system has no notion of current directory, or the
589 current directory cannot be dynamically changed.
591 * 'InappropriateType'
592 The path refers to an existing non-directory object.
597 setCurrentDirectory :: FilePath -> IO ()
598 setCurrentDirectory path = do
599 modifyIOError (`ioeSetFileName` path) $
600 withCString path $ \s ->
601 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
602 -- ToDo: add path to error
604 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
605 exists and is a directory, and 'False' otherwise.
608 doesDirectoryExist :: FilePath -> IO Bool
609 doesDirectoryExist name =
611 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
612 (\ _ -> return False)
614 {- |The operation 'doesFileExist' returns 'True'
615 if the argument file exists and is not a directory, and 'False' otherwise.
618 doesFileExist :: FilePath -> IO Bool
619 doesFileExist name = do
621 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
622 (\ _ -> return False)
624 {- |The 'getModificationTime' operation returns the
625 clock time at which the file or directory was last modified.
627 The operation may fail with:
629 * 'isPermissionError' if the user is not permitted to access
630 the modification time; or
632 * 'isDoesNotExistError' if the file or directory does not exist.
636 getModificationTime :: FilePath -> IO ClockTime
637 getModificationTime name =
638 withFileStatus "getModificationTime" name $ \ st ->
641 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
642 withFileStatus loc name f = do
643 modifyIOError (`ioeSetFileName` name) $
644 allocaBytes sizeof_stat $ \p ->
645 withCString (fileNameEndClean name) $ \s -> do
646 throwErrnoIfMinus1Retry_ loc (c_stat s p)
649 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
650 withFileOrSymlinkStatus loc name f = do
651 modifyIOError (`ioeSetFileName` name) $
652 allocaBytes sizeof_stat $ \p ->
653 withCString name $ \s -> do
654 throwErrnoIfMinus1Retry_ loc (lstat s p)
657 modificationTime :: Ptr CStat -> IO ClockTime
658 modificationTime stat = do
659 mtime <- st_mtime stat
660 let realToInteger = round . realToFrac :: Real a => a -> Integer
661 return (TOD (realToInteger (mtime :: CTime)) 0)
663 isDirectory :: Ptr CStat -> IO Bool
664 isDirectory stat = do
666 return (s_isdir mode)
668 fileNameEndClean :: String -> String
669 fileNameEndClean name =
670 if i > 0 && (ec == '\\' || ec == '/') then
671 fileNameEndClean (take i name)
675 i = (length name) - 1
678 foreign import ccall unsafe "__hscore_long_path_size"
679 long_path_size :: Int
681 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
682 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
683 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
685 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
686 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
687 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode