2014-04-15 3 views
2

하스 no 멍에 여기. 나는 하스켈의 적절한 사용과 관련하여 좀 더 근본적인 측면을 이끌어 낼 수있는 기존의 라이브러리를 사용하는 방법에 관한 질문을 가지고있다.Codec.Archive.Tar의 기능을 재정의하는 방법

저는 배울 때 하스켈을 배우며 작 업할 생각이 있습니다. 스크립트는 주어진 디렉토리에서 모든 tarball을 찾아서 병렬로 풀어야합니다. 이 시점에서, 나는 언 패킹의 기본 기능을 다루고 있습니다. 따라서 Codec.Archive.Tar 패키지를 사용하여 tarball과 관련된 동작을 정규화 된 경로로 재정의 할 수 있습니까? 내가 함께 포장 된 타르볼이있는 디렉토리에서이 프로그램을 실행할 때

module Main where 

import qualified Codec.Archive.Tar as Tar 
import qualified Codec.Compression.GZip as GZip 
import Control.Monad (liftM, unless) 
import qualified Data.ByteString.Lazy as BS 
import System.Directory (doesDirectoryExist, getDirectoryContents) 
import System.Exit (exitWith, ExitCode(..)) 
import System.FilePath.Posix (takeExtension) 

searchPath = "/home/someuser/tarball/dir" 

exit = exitWith ExitSuccess 
die = exitWith (ExitFailure 1) 

processFile :: String -> IO() 
processFile file = do 
    putStrLn $ "Unpacking " ++ file ++ " to " ++ searchPath 
    Tar.unpack searchPath . Tar.read . GZip.decompress =<< BS.readFile filePath 
    where filePath = searchPath ++ "/" ++ file 

main = do 
    dirExists <- doesDirectoryExist searchPath 
    unless dirExists $ (putStrLn $ "Error: Search path not found: " ++ searchPath) >> die 
    files <- targetFiles `liftM` getDirectoryContents searchPath 
    mapM_ processFile files 
    exit 
    where targetFiles = filter (\f -> f /= "." && f /= ".." && takeExtension f == ".tgz") 

: 여기

몇 가지 예제 코드입니다

tar czvPf myfile.tgz /tarball_testing/myfile 

내가 얻을 다음과 같은 출력 :

Unpacking myfile.tgz to /tarball_testing 
unpacker.hs: Absolute file name in tar archive: "/tarball_testing/myfile" 

두 번째 줄이 문제입니다. 에 대한 문서 읽기이 기능을 사용하지 않도록 설정할 방법이 없습니다 (타르볼에서 전체 경로를 사용하려는 이유 또는 이와 관련된 보안 관련 의미에 대한 토론에 관심이 없음).

내가 생각하기에 가장 먼저 떠오르는 점은 어떻게 든 함수를 재정의해야한다는 것이지만 Haskeller가하는 것처럼 느껴지지 않는다는 것입니다. 올바른 방향으로 포인터를 가져올 수 있습니까?

+0

: 걱정한다고 생각하지 않습니다 패키지] (http://hackage.haskell.org/package/tar) 파일을 추출하기 위해 제공된 유일한 인터페이스가 절대 경로를 거부하는 것처럼 보입니다. 그 패키지로 당신이 할 수있는 일은 아무것도 없습니다. – Carl

+0

사용할 수있는 한 가지는 ['mapEntries'] (http://hackage.haskell.org/package/tar-0.3.1.0/docs/Codec-Archive-Tar.html#v:mapEntries)와 ['Codec.Archive.Tar.Entry'] (http://hackage.haskell.org/package/tar-0.3.1.0/docs/Codec-Archive-Tar-Entry.html)와'System.FilePath'는 추출 전에 상대 경로 항목. – duplode

답변

3

당신은 monkey patch을 할 수 없거나 Haskell 모듈의 함수를 오버라이드 할 수 없기 때문에 라이브러리의 안전 조치를 피할 수있는 해결 방법은 없습니다. 그러나 수행 할 수있는 작업은 Codec.Archive.Tar의 기능을 사용하여 압축을 풀기 전에 tar 항목 경로를 수정하여 더 이상 절대 경로가되지 않도록하는 것입니다. Entry 개별 항목의 타입이 동시에 구체적 유형

mapEntriesNoFail :: (Entry -> Entry) -> Entries e -> Entries e 

EntriesmapEntriesNoFail 함수 Tar.unpack의 인수의 형태가된다. mapEntriesNoFail 덕분에 우리의 문제는 경로를 조정하는 Entry -> Entry 함수를 작성하게됩니다.

import qualified Codec.Archive.Tar.Entry as Tar 
import System.FilePath.Posix (takeExtension, dropDrive, hasTrailingPathSeparator) 
import Data.Either (either) 

기능은 다음과 같을 수 있습니다 :이를 위해 먼저 우리는 몇 가지 추가적인 수입이 필요합니다이 좀 장황한를 보일 수

dropDriveFromEntry :: Tar.Entry -> Tar.Entry 
dropDriveFromEntry entry = 
    either (error "Resulting tar path is somehow too long") 
     (\tp -> entry { Tar.entryTarPath = tp }) 
     drivelessTarPath 
    where 
    tarPath = Tar.entryTarPath entry 
    path = Tar.fromTarPath tarPath 
    toTarPath' p = Tar.toTarPath (hasTrailingPathSeparator p) p 
    drivelessTarPath = toTarPath' $ dropDrive path 

을; 그러나, 우리가 뛰어 넘는 농구는 결과로 나온 타르 경로가 제정신이되도록 보장합니다. Codec.Archive.Tar.Entry 설명서에서 tar 처리에 대한 자세한 내용을 읽을 수 있습니다. 이 정의의 핵심 기능은 절대 경로 상대 경로를 만드는 dropDrive입니다 (Linux에서는 절대 경로의 슬래시를 제거합니다).

either의 사용에 대해 약간의 단어를 쓰는 것이 좋습니다. toTarPath은 실패 가능성을 설명하기 위해 Either String TarPath 유형의 값을 생성합니다. 특히 제공된 경로가 너무 길면 tar 경로로의 변환이 실패합니다. 그러나 우리의 경우 경로는 너무 길 수 없습니다. 이미 경로가 tar 파일에 있었으므로 제거 된 선행 슬래시가있을 수 있습니다.그렇기 때문에 either을 사용하여 Either 랩핑을 제거하고 (불가능한) Left 경우를 처리하는 함수 대신 오류를 전달하는 것으로 충분합니다.

dropDriveFromEntry을 사용하는 중, 압축을 풀기 전에 항목 위에 매핑하면됩니다. 프로그램의 관련 라인이 될 것입니다 : dropDriveFromEntry에서 설명 될 수있는 관련 오류가 있다면, 우리가 Either String TarPath를 돌려 확인한 다음 mapEntriesNoFail 대신 mapEntries를 사용하는 것이

Tar.unpack searchPath . Tar.mapEntriesNoFail dropDriveFromEntry 
     . Tar.read . GZip.decompress =<< BS.readFile filePath 

참고.

이러한 변경 사항을 적용하면 tar 파일의 항목이 /home/someuser/tarball/dir/tarball_testing/myfile (으)로 추출됩니다. 원하는 내용이 아닌 경우 dropDriveFromEntry을 수정하여 필요한 추가 경로 처리를 수행 할 수 있습니다.

는 PS : 당신의 질문의 제목에 관해서는, 당신이 우리를 보여준 재치있는 작은 프로그램을 고려, 나는 당신이 [`tar`에 대한 문서 내 검색에서

관련 문제