80f0beabbd4fa7cae9a1e3a38876ffd163ae5ec8
[packages/unix.git] / System / Posix / Files / ByteString.hsc
1 #ifdef __GLASGOW_HASKELL__
2 {-# LANGUAGE Trustworthy #-}
3 #endif
4 -----------------------------------------------------------------------------
5 -- |
6 -- Module      :  System.Posix.Files.ByteString
7 -- Copyright   :  (c) The University of Glasgow 2002
8 -- License     :  BSD-style (see the file libraries/base/LICENSE)
9 -- 
10 -- Maintainer  :  libraries@haskell.org
11 -- Stability   :  provisional
12 -- Portability :  non-portable (requires POSIX)
13 --
14 -- Functions defined by the POSIX standards for manipulating and querying the
15 -- file system. Names of underlying POSIX functions are indicated whenever
16 -- possible. A more complete documentation of the POSIX functions together
17 -- with a more detailed description of different error conditions are usually
18 -- available in the system's manual pages or from
19 -- <http://www.unix.org/version3/online.html> (free registration required).
20 --
21 -- When a function that calls an underlying POSIX function fails, the errno
22 -- code is converted to an 'IOError' using 'Foreign.C.Error.errnoToIOError'.
23 -- For a list of which errno codes may be generated, consult the POSIX
24 -- documentation for the underlying function.
25 --
26 -----------------------------------------------------------------------------
27
28 #include "HsUnix.h"
29
30 module System.Posix.Files.ByteString (
31     -- * File modes
32     -- FileMode exported by System.Posix.Types
33     unionFileModes, intersectFileModes,
34     nullFileMode,
35     ownerReadMode, ownerWriteMode, ownerExecuteMode, ownerModes,
36     groupReadMode, groupWriteMode, groupExecuteMode, groupModes,
37     otherReadMode, otherWriteMode, otherExecuteMode, otherModes,
38     setUserIDMode, setGroupIDMode,
39     stdFileMode,   accessModes,
40     fileTypeModes,
41     blockSpecialMode, characterSpecialMode, namedPipeMode, regularFileMode,
42     directoryMode, symbolicLinkMode, socketMode,
43
44     -- ** Setting file modes
45     setFileMode, setFdMode, setFileCreationMask,
46
47     -- ** Checking file existence and permissions
48     fileAccess, fileExist,
49
50     -- * File status
51     FileStatus,
52     -- ** Obtaining file status
53     getFileStatus, getFdStatus, getSymbolicLinkStatus,
54     -- ** Querying file status
55     deviceID, fileID, fileMode, linkCount, fileOwner, fileGroup,
56     specialDeviceID, fileSize, accessTime, modificationTime,
57     statusChangeTime,
58     accessTimeHiRes, modificationTimeHiRes, statusChangeTimeHiRes,
59     isBlockDevice, isCharacterDevice, isNamedPipe, isRegularFile,
60     isDirectory, isSymbolicLink, isSocket,
61
62     -- * Creation
63     createNamedPipe, 
64     createDevice,
65
66     -- * Hard links
67     createLink, removeLink,
68
69     -- * Symbolic links
70     createSymbolicLink, readSymbolicLink,
71
72     -- * Renaming files
73     rename,
74
75     -- * Changing file ownership
76     setOwnerAndGroup,  setFdOwnerAndGroup,
77 #if HAVE_LCHOWN
78     setSymbolicLinkOwnerAndGroup,
79 #endif
80
81     -- * Changing file timestamps
82     setFileTimes, setFileTimesHiRes,
83     setFdTimesHiRes, setSymbolicLinkTimesHiRes,
84     touchFile, touchFd, touchSymbolicLink,
85
86     -- * Setting file sizes
87     setFileSize, setFdSize,
88
89     -- * Find system-specific limits for a file
90     PathVar(..), getPathVar, getFdPathVar,
91   ) where
92
93 import System.Posix.Types
94 import System.Posix.Internals hiding (withFilePath, peekFilePathLen)
95 import Foreign
96 import Foreign.C hiding (
97      throwErrnoPath,
98      throwErrnoPathIf,
99      throwErrnoPathIf_,
100      throwErrnoPathIfNull,
101      throwErrnoPathIfMinus1,
102      throwErrnoPathIfMinus1_ )
103
104 import System.Posix.Files.Common
105 import System.Posix.ByteString.FilePath
106
107 import Data.Time.Clock.POSIX (POSIXTime)
108
109 -- -----------------------------------------------------------------------------
110 -- chmod()
111
112 -- | @setFileMode path mode@ changes permission of the file given by @path@
113 -- to @mode@. This operation may fail with 'throwErrnoPathIfMinus1_' if @path@
114 -- doesn't exist or if the effective user ID of the current process is not that
115 -- of the file's owner.
116 --
117 -- Note: calls @chmod@.
118 setFileMode :: RawFilePath -> FileMode -> IO ()
119 setFileMode name m =
120   withFilePath name $ \s -> do
121     throwErrnoPathIfMinus1_ "setFileMode" name (c_chmod s m)
122
123
124 -- -----------------------------------------------------------------------------
125 -- access()
126
127 -- | @fileAccess name read write exec@ checks if the file (or other file system
128 -- object) @name@ can be accessed for reading, writing and\/or executing. To
129 -- check a permission set the corresponding argument to 'True'.
130 --
131 -- Note: calls @access@.
132 fileAccess :: RawFilePath -> Bool -> Bool -> Bool -> IO Bool
133 fileAccess name readOK writeOK execOK = access name flags
134   where
135    flags   = read_f .|. write_f .|. exec_f
136    read_f  = if readOK  then (#const R_OK) else 0
137    write_f = if writeOK then (#const W_OK) else 0
138    exec_f  = if execOK  then (#const X_OK) else 0
139
140 -- | Checks for the existence of the file.
141 --
142 -- Note: calls @access@.
143 fileExist :: RawFilePath -> IO Bool
144 fileExist name = 
145   withFilePath name $ \s -> do
146     r <- c_access s (#const F_OK)
147     if (r == 0)
148         then return True
149         else do err <- getErrno
150                 if (err == eNOENT)
151                    then return False
152                    else throwErrnoPath "fileExist" name
153
154 access :: RawFilePath -> CMode -> IO Bool
155 access name flags = 
156   withFilePath name $ \s -> do
157     r <- c_access s (fromIntegral flags)
158     if (r == 0)
159         then return True
160         else do err <- getErrno
161                 if (err == eACCES)
162                    then return False
163                    else throwErrnoPath "fileAccess" name
164
165
166 -- | @getFileStatus path@ calls gets the @FileStatus@ information (user ID,
167 -- size, access times, etc.) for the file @path@.
168 --
169 -- Note: calls @stat@.
170 getFileStatus :: RawFilePath -> IO FileStatus
171 getFileStatus path = do
172   fp <- mallocForeignPtrBytes (#const sizeof(struct stat)) 
173   withForeignPtr fp $ \p ->
174     withFilePath path $ \s -> 
175       throwErrnoPathIfMinus1Retry_ "getFileStatus" path (c_stat s p)
176   return (FileStatus fp)
177
178 -- | Acts as 'getFileStatus' except when the 'RawFilePath' refers to a symbolic
179 -- link. In that case the @FileStatus@ information of the symbolic link itself
180 -- is returned instead of that of the file it points to.
181 --
182 -- Note: calls @lstat@.
183 getSymbolicLinkStatus :: RawFilePath -> IO FileStatus
184 getSymbolicLinkStatus path = do
185   fp <- mallocForeignPtrBytes (#const sizeof(struct stat)) 
186   withForeignPtr fp $ \p ->
187     withFilePath path $ \s -> 
188       throwErrnoPathIfMinus1_ "getSymbolicLinkStatus" path (c_lstat s p)
189   return (FileStatus fp)
190
191 foreign import ccall unsafe "__hsunix_lstat"
192   c_lstat :: CString -> Ptr CStat -> IO CInt
193
194 -- | @createNamedPipe fifo mode@
195 -- creates a new named pipe, @fifo@, with permissions based on
196 -- @mode@. May fail with 'throwErrnoPathIfMinus1_' if a file named @name@
197 -- already exists or if the effective user ID of the current process doesn't
198 -- have permission to create the pipe.
199 --
200 -- Note: calls @mkfifo@.
201 createNamedPipe :: RawFilePath -> FileMode -> IO ()
202 createNamedPipe name mode = do
203   withFilePath name $ \s -> 
204     throwErrnoPathIfMinus1_ "createNamedPipe" name (c_mkfifo s mode)
205
206 -- | @createDevice path mode dev@ creates either a regular or a special file
207 -- depending on the value of @mode@ (and @dev@).  @mode@ will normally be either
208 -- 'blockSpecialMode' or 'characterSpecialMode'.  May fail with
209 -- 'throwErrnoPathIfMinus1_' if a file named @name@ already exists or if the
210 -- effective user ID of the current process doesn't have permission to create
211 -- the file.
212 --
213 -- Note: calls @mknod@.
214 createDevice :: RawFilePath -> FileMode -> DeviceID -> IO ()
215 createDevice path mode dev =
216   withFilePath path $ \s ->
217     throwErrnoPathIfMinus1_ "createDevice" path (c_mknod s mode dev)
218
219 foreign import ccall unsafe "__hsunix_mknod" 
220   c_mknod :: CString -> CMode -> CDev -> IO CInt
221
222 -- -----------------------------------------------------------------------------
223 -- Hard links
224
225 -- | @createLink old new@ creates a new path, @new@, linked to an existing file,
226 -- @old@.
227 --
228 -- Note: calls @link@.
229 createLink :: RawFilePath -> RawFilePath -> IO ()
230 createLink name1 name2 =
231   withFilePath name1 $ \s1 ->
232   withFilePath name2 $ \s2 ->
233   throwErrnoPathIfMinus1_ "createLink" name1 (c_link s1 s2)
234
235 -- | @removeLink path@ removes the link named @path@.
236 --
237 -- Note: calls @unlink@.
238 removeLink :: RawFilePath -> IO ()
239 removeLink name =
240   withFilePath name $ \s ->
241   throwErrnoPathIfMinus1_ "removeLink" name (c_unlink s)
242
243 -- -----------------------------------------------------------------------------
244 -- Symbolic Links
245
246 -- | @createSymbolicLink file1 file2@ creates a symbolic link named @file2@
247 -- which points to the file @file1@.
248 --
249 -- Symbolic links are interpreted at run-time as if the contents of the link
250 -- had been substituted into the path being followed to find a file or directory.
251 --
252 -- Note: calls @symlink@.
253 createSymbolicLink :: RawFilePath -> RawFilePath -> IO ()
254 createSymbolicLink file1 file2 =
255   withFilePath file1 $ \s1 ->
256   withFilePath file2 $ \s2 ->
257   throwErrnoPathIfMinus1_ "createSymbolicLink" file1 (c_symlink s1 s2)
258
259 foreign import ccall unsafe "symlink"
260   c_symlink :: CString -> CString -> IO CInt
261
262 -- ToDo: should really use SYMLINK_MAX, but not everyone supports it yet,
263 -- and it seems that the intention is that SYMLINK_MAX is no larger than
264 -- PATH_MAX.
265 #if !defined(PATH_MAX)
266 -- PATH_MAX is not defined on systems with unlimited path length.
267 -- Ugly.  Fix this.
268 #define PATH_MAX 4096
269 #endif
270
271 -- | Reads the @RawFilePath@ pointed to by the symbolic link and returns it.
272 --
273 -- Note: calls @readlink@.
274 readSymbolicLink :: RawFilePath -> IO RawFilePath
275 readSymbolicLink file =
276   allocaArray0 (#const PATH_MAX) $ \buf -> do
277     withFilePath file $ \s -> do
278       len <- throwErrnoPathIfMinus1 "readSymbolicLink" file $ 
279         c_readlink s buf (#const PATH_MAX)
280       peekFilePathLen (buf,fromIntegral len)
281
282 foreign import ccall unsafe "readlink"
283   c_readlink :: CString -> CString -> CSize -> IO CInt
284
285 -- -----------------------------------------------------------------------------
286 -- Renaming files
287
288 -- | @rename old new@ renames a file or directory from @old@ to @new@.
289 --
290 -- Note: calls @rename@.
291 rename :: RawFilePath -> RawFilePath -> IO ()
292 rename name1 name2 =
293   withFilePath name1 $ \s1 ->
294   withFilePath name2 $ \s2 ->
295   throwErrnoPathIfMinus1_ "rename" name1 (c_rename s1 s2)
296
297 foreign import ccall unsafe "rename"
298    c_rename :: CString -> CString -> IO CInt
299
300 -- -----------------------------------------------------------------------------
301 -- chown()
302
303 -- | @setOwnerAndGroup path uid gid@ changes the owner and group of @path@ to
304 -- @uid@ and @gid@, respectively.
305 --
306 -- If @uid@ or @gid@ is specified as -1, then that ID is not changed.
307 --
308 -- Note: calls @chown@.
309 setOwnerAndGroup :: RawFilePath -> UserID -> GroupID -> IO ()
310 setOwnerAndGroup name uid gid = do
311   withFilePath name $ \s ->
312     throwErrnoPathIfMinus1_ "setOwnerAndGroup" name (c_chown s uid gid)
313
314 foreign import ccall unsafe "chown"
315   c_chown :: CString -> CUid -> CGid -> IO CInt
316
317 #if HAVE_LCHOWN
318 -- | Acts as 'setOwnerAndGroup' but does not follow symlinks (and thus
319 -- changes permissions on the link itself).
320 --
321 -- Note: calls @lchown@.
322 setSymbolicLinkOwnerAndGroup :: RawFilePath -> UserID -> GroupID -> IO ()
323 setSymbolicLinkOwnerAndGroup name uid gid = do
324   withFilePath name $ \s ->
325     throwErrnoPathIfMinus1_ "setSymbolicLinkOwnerAndGroup" name
326         (c_lchown s uid gid)
327
328 foreign import ccall unsafe "lchown"
329   c_lchown :: CString -> CUid -> CGid -> IO CInt
330 #endif
331
332 -- -----------------------------------------------------------------------------
333 -- Setting file times
334
335 -- | @setFileTimes path atime mtime@ sets the access and modification times
336 -- associated with file @path@ to @atime@ and @mtime@, respectively.
337 --
338 -- Note: calls @utime@.
339 setFileTimes :: RawFilePath -> EpochTime -> EpochTime -> IO ()
340 setFileTimes name atime mtime = do
341   withFilePath name $ \s ->
342    allocaBytes (#const sizeof(struct utimbuf)) $ \p -> do
343      (#poke struct utimbuf, actime)  p atime
344      (#poke struct utimbuf, modtime) p mtime
345      throwErrnoPathIfMinus1_ "setFileTimes" name (c_utime s p)
346
347 -- | Like 'setFileTimes' but timestamps can have sub-second resolution.
348 --
349 -- Note: calls @utimensat@ or @utimes@.
350 setFileTimesHiRes :: RawFilePath -> POSIXTime -> POSIXTime -> IO ()
351 #ifdef HAVE_UTIMENSAT
352 setFileTimesHiRes name atime mtime =
353   withFilePath name $ \s ->
354     withArray [toCTimeSpec atime, toCTimeSpec mtime] $ \times ->
355       throwErrnoPathIfMinus1_ "setFileTimesHiRes" name $
356         c_utimensat (#const AT_FDCWD) s times 0
357 #else
358 setFileTimesHiRes name atime mtime =
359   withFilePath name $ \s ->
360     withArray [toCTimeVal atime, toCTimeVal mtime] $ \times ->
361       throwErrnoPathIfMinus1_ "setFileTimesHiRes" name (c_utimes s times)
362 #endif
363
364 -- | Like 'setFileTimesHiRes' but does not follow symbolic links.
365 -- This operation is not supported on all platforms. On these platforms,
366 -- this function will raise an exception.
367 --
368 -- Note: calls @utimensat@ or @lutimes@.
369 setSymbolicLinkTimesHiRes :: RawFilePath -> POSIXTime -> POSIXTime -> IO ()
370 #if HAVE_UTIMENSAT
371 setSymbolicLinkTimesHiRes name atime mtime =
372   withFilePath name $ \s ->
373     withArray [toCTimeSpec atime, toCTimeSpec mtime] $ \times ->
374       throwErrnoPathIfMinus1_ "setSymbolicLinkTimesHiRes" name $
375         c_utimensat (#const AT_FDCWD) s times (#const AT_SYMLINK_NOFOLLOW)
376 #elif HAVE_LUTIMES
377 setSymbolicLinkTimesHiRes name atime mtime =
378   withFilePath name $ \s ->
379     withArray [toCTimeVal atime, toCTimeVal mtime] $ \times ->
380       throwErrnoPathIfMinus1_ "setSymbolicLinkTimesHiRes" name $
381         c_lutimes s times
382 #else
383 setSymbolicLinkTimesHiRes =
384   error "setSymbolicLinkTimesHiRes: not available on this platform"
385 #endif
386
387 -- | @touchFile path@ sets the access and modification times associated with
388 -- file @path@ to the current time.
389 --
390 -- Note: calls @utime@.
391 touchFile :: RawFilePath -> IO ()
392 touchFile name = do
393   withFilePath name $ \s ->
394    throwErrnoPathIfMinus1_ "touchFile" name (c_utime s nullPtr)
395
396 -- | Like 'touchFile' but does not follow symbolic links.
397 -- This operation is not supported on all platforms. On these platforms,
398 -- this function will raise an exception.
399 --
400 -- Note: calls @lutimes@.
401 touchSymbolicLink :: RawFilePath -> IO ()
402 #if HAVE_LUTIMES
403 touchSymbolicLink name =
404   withFilePath name $ \s ->
405     throwErrnoPathIfMinus1_ "touchSymbolicLink" name (c_lutimes s nullPtr)
406 #else
407 touchSymbolicLink =
408   error "touchSymbolicLink: not available on this platform"
409 #endif
410
411 -- -----------------------------------------------------------------------------
412 -- Setting file sizes
413
414 -- | Truncates the file down to the specified length. If the file was larger
415 -- than the given length before this operation was performed the extra is lost.
416 --
417 -- Note: calls @truncate@.
418 setFileSize :: RawFilePath -> FileOffset -> IO ()
419 setFileSize file off = 
420   withFilePath file $ \s ->
421     throwErrnoPathIfMinus1_ "setFileSize" file (c_truncate s off)
422
423 foreign import ccall unsafe "truncate"
424   c_truncate :: CString -> COff -> IO CInt
425
426 -- -----------------------------------------------------------------------------
427 -- pathconf()/fpathconf() support
428
429 -- | @getPathVar var path@ obtains the dynamic value of the requested
430 -- configurable file limit or option associated with file or directory @path@.
431 -- For defined file limits, @getPathVar@ returns the associated
432 -- value.  For defined file options, the result of @getPathVar@
433 -- is undefined, but not failure.
434 --
435 -- Note: calls @pathconf@.
436 getPathVar :: RawFilePath -> PathVar -> IO Limit
437 getPathVar name v = do
438   withFilePath name $ \ nameP -> 
439     throwErrnoPathIfMinus1 "getPathVar" name $ 
440       c_pathconf nameP (pathVarConst v)
441
442 foreign import ccall unsafe "pathconf" 
443   c_pathconf :: CString -> CInt -> IO CLong