@@ -38,7 +38,7 @@ import qualified System.FilePath as FilePath.Native
38
38
import System.Directory
39
39
( listDirectory , doesDirectoryExist , getModificationTime
40
40
, pathIsSymbolicLink , getSymbolicLinkTarget
41
- , Permissions (.. ), getPermissions )
41
+ , Permissions (.. ), getPermissions , getFileSize )
42
42
import Data.Time.Clock
43
43
( UTCTime )
44
44
import Data.Time.Clock.POSIX
@@ -141,14 +141,33 @@ packFileEntry
141
141
packFileEntry filepath tarpath = do
142
142
mtime <- getModTime filepath
143
143
perms <- getPermissions filepath
144
- file <- openBinaryFile filepath ReadMode
145
- size <- hFileSize file
146
- content <- BL. hGetContents file
147
- return (simpleEntry tarpath (NormalFile content (fromIntegral size))) {
148
- entryPermissions = if executable perms then executableFilePermissions
149
- else ordinaryFilePermissions,
150
- entryTime = mtime
151
- }
144
+ -- Get file size without opening it.
145
+ approxSize <- getFileSize filepath
146
+
147
+ (content, size) <- if approxSize < 131072
148
+ -- If file is short enough, just read it strictly
149
+ -- so that no file handle dangles around indefinitely.
150
+ then do
151
+ cnt <- B. readFile filepath
152
+ pure (BL. fromStrict cnt, fromIntegral $ B. length cnt)
153
+ else do
154
+ hndl <- openBinaryFile filepath ReadMode
155
+ -- File size could have changed between measuring approxSize
156
+ -- and here. Measuring again.
157
+ sz <- hFileSize hndl
158
+ -- Lazy I/O at its best: once cnt is forced in full,
159
+ -- BL.hGetContents will close the handle.
160
+ cnt <- BL. hGetContents hndl
161
+ -- It would be wrong to return (cnt, BL.length sz):
162
+ -- NormalFile constructor below forces size which in turn
163
+ -- allocates entire cnt in memory at once.
164
+ pure (cnt, fromInteger sz)
165
+
166
+ pure (simpleEntry tarpath (NormalFile content size))
167
+ { entryPermissions =
168
+ if executable perms then executableFilePermissions else ordinaryFilePermissions
169
+ , entryTime = mtime
170
+ }
152
171
153
172
-- | Construct a tar 'Entry' based on a local directory (but not its contents).
154
173
--
0 commit comments