Skip to content

Commit 9944d96

Browse files
authored
Merge pull request #286 from sandydoo/fix-case-hack
nar: fix ordering of case-hacked paths on macOS
2 parents 247be71 + 67ea0d0 commit 9944d96

File tree

3 files changed

+26
-21
lines changed

3 files changed

+26
-21
lines changed

hnix-store-nar/src/System/Nix/Nar/Options.hs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ data NarOptions = NarOptions {
2020

2121
defaultNarOptions :: NarOptions
2222
defaultNarOptions = NarOptions {
23-
optUseCaseHack =
24-
if System.Info.os == "darwin"
25-
then True
26-
else False
23+
optUseCaseHack = System.Info.os == "darwin"
2724
}
2825

2926
caseHackSuffix :: Text

hnix-store-nar/src/System/Nix/Nar/Parser.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ parseDirectory = do
320320
conflictCount <- getFilePathConflictCount key
321321
pure $
322322
if conflictCount > 0 then
323-
fName <> Nar.caseHackSuffix <> (Text.pack $ show conflictCount)
323+
fName <> Nar.caseHackSuffix <> Text.pack (show conflictCount)
324324
else
325325
fName
326326

hnix-store-nar/src/System/Nix/Nar/Streamer.hs

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module System.Nix.Nar.Streamer
1212

1313
import Data.ByteString (ByteString)
1414
import Data.Int (Int64)
15-
import Data.Text (Text)
15+
import qualified Data.Map.Strict as Map
1616

1717
import Control.Monad ( forM_
1818
, when
@@ -23,7 +23,7 @@ import qualified Data.ByteString.Lazy as Bytes.Lazy
2323
import qualified Data.Foldable
2424
import qualified Data.List
2525
import qualified Data.Serialize as Serial
26-
import qualified Data.Text as T (pack, breakOn)
26+
import qualified Data.Text as T (pack, unpack)
2727
import qualified Data.Text.Encoding as TE (encodeUtf8)
2828
import System.FilePath ((</>))
2929

@@ -92,18 +92,24 @@ streamNarIOWithOptions opts effs basePath yield = do
9292
isDir <- IO.liftIO $ Nar.narIsDir effs path
9393
if isDir then do
9494
fs <- IO.liftIO (Nar.narListDir effs path)
95+
let entries =
96+
foldr (\f acc ->
97+
let
98+
name =
99+
if Nar.optUseCaseHack opts
100+
then undoCaseHack f
101+
else f
102+
in
103+
case Map.insertLookupWithKey (\_ n _ -> n) name f acc of
104+
(Nothing, newMap) -> newMap
105+
(Just conflict, _) -> error $ "File name collision between " ++ (path </> name) ++ " and " ++ (path </> conflict)
106+
) Map.empty fs
95107
yield $ strs ["type", "directory"]
96-
forM_ (Data.List.sort fs) $ \f -> do
108+
forM_ (Map.toAscList entries) $ \(unhacked, original) -> do
97109
yield $ str "entry"
98110
parens $ do
99-
let fullName = path </> f
100-
let serializedPath =
101-
if Nar.optUseCaseHack opts then
102-
filePathToBSWithCaseHack f
103-
else
104-
filePathToBS f
105-
yield $ strs ["name", serializedPath, "node"]
106-
parens $ go fullName
111+
yield $ strs ["name", filePathToBS unhacked, "node"]
112+
parens $ go (path </> original)
107113
else do
108114
isExec <- IO.liftIO $ Nar.narIsExec effs path
109115
yield $ strs ["type", "regular"]
@@ -148,8 +154,10 @@ strs xs = Bytes.concat $ str <$> xs
148154
filePathToBS :: FilePath -> ByteString
149155
filePathToBS = TE.encodeUtf8 . T.pack
150156

151-
filePathToBSWithCaseHack :: FilePath -> ByteString
152-
filePathToBSWithCaseHack = TE.encodeUtf8 . undoCaseHack . T.pack
153-
154-
undoCaseHack :: Text -> Text
155-
undoCaseHack = fst . T.breakOn Nar.caseHackSuffix
157+
undoCaseHack :: FilePath -> FilePath
158+
undoCaseHack f =
159+
case Data.List.findIndex (caseHackSuffix `Data.List.isPrefixOf`) (Data.List.tails f) of
160+
Just index -> take index f
161+
Nothing -> f
162+
where
163+
caseHackSuffix = T.unpack Nar.caseHackSuffix

0 commit comments

Comments
 (0)