Skip to content

Commit 0faa59b

Browse files
committed
Pack: more elaborate fix to alleviate leakage of file handles
1 parent 7d694a0 commit 0faa59b

File tree

1 file changed

+28
-9
lines changed

1 file changed

+28
-9
lines changed

Codec/Archive/Tar/Pack.hs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import qualified System.FilePath as FilePath.Native
3838
import System.Directory
3939
( listDirectory, doesDirectoryExist, getModificationTime
4040
, pathIsSymbolicLink, getSymbolicLinkTarget
41-
, Permissions(..), getPermissions )
41+
, Permissions(..), getPermissions, getFileSize )
4242
import Data.Time.Clock
4343
( UTCTime )
4444
import Data.Time.Clock.POSIX
@@ -141,14 +141,33 @@ packFileEntry
141141
packFileEntry filepath tarpath = do
142142
mtime <- getModTime filepath
143143
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+
}
152171

153172
-- | Construct a tar 'Entry' based on a local directory (but not its contents).
154173
--

0 commit comments

Comments
 (0)