1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 1997-2011. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: -module(tar_SUITE). 20: 21: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 22: init_per_group/2,end_per_group/2, borderline/1, atomic/1, long_names/1, 23: create_long_names/1, bad_tar/1, errors/1, extract_from_binary/1, 24: extract_from_binary_compressed/1, 25: extract_from_open_file/1, symlinks/1, open_add_close/1, cooked_compressed/1, 26: memory/1]). 27: 28: -include_lib("test_server/include/test_server.hrl"). 29: -include_lib("kernel/include/file.hrl"). 30: 31: suite() -> [{ct_hooks,[ts_install_cth]}]. 32: 33: all() -> 34: [borderline, atomic, long_names, create_long_names, 35: bad_tar, errors, extract_from_binary, 36: extract_from_binary_compressed, extract_from_open_file, 37: symlinks, open_add_close, cooked_compressed, memory]. 38: 39: groups() -> 40: []. 41: 42: init_per_suite(Config) -> 43: Config. 44: 45: end_per_suite(_Config) -> 46: ok. 47: 48: init_per_group(_GroupName, Config) -> 49: Config. 50: 51: end_per_group(_GroupName, Config) -> 52: Config. 53: 54: 55: borderline(doc) -> 56: ["Test creating, listing and extracting one file from an archive", 57: "multiple times with different file sizes. ", 58: "Also check that the file attributes of the extracted file has survived."]; 59: borderline(Config) when is_list(Config) -> 60: 61: %% Note: We cannot use absolute paths, because the pathnames will be 62: %% too long for the limit allowed in tar files (100 characters). 63: %% Therefore, strip off the current working directory from the front 64: %% of the private directory path. 65: 66: ?line {ok, Cwd} = file:get_cwd(), 67: ?line RootDir = ?config(priv_dir, Config), 68: ?line TempDir = remove_prefix(Cwd++"/", filename:join(RootDir, "borderline")), 69: ?line ok = file:make_dir(TempDir), 70: 71: ?line Record = 512, 72: ?line Block = 20 * Record, 73: 74: ?line lists:foreach(fun(Size) -> borderline_test(Size, TempDir) end, 75: [0, 1, 10, 13, 127, 333, Record-1, Record, Record+1, 76: Block-Record-1, Block-Record, Block-Record+1, 77: Block-1, Block, Block+1, 78: Block+Record-1, Block+Record, Block+Record+1]), 79: 80: %% Clean up. 81: ?line delete_files([TempDir]), 82: 83: ok. 84: 85: borderline_test(Size, TempDir) -> 86: ?line Archive = filename:join(TempDir, "ar_"++integer_to_list(Size)++".tar"), 87: ?line Name = filename:join(TempDir, "file_"++integer_to_list(Size)), 88: ?line io:format("Testing size ~p", [Size]), 89: 90: %% Create a file and archive it. 91: ?line {_, _, X0} = erlang:now(), 92: ?line file:write_file(Name, random_byte_list(X0, Size)), 93: ?line ok = erl_tar:create(Archive, [Name]), 94: ?line ok = file:delete(Name), 95: 96: %% Verify listing and extracting. 97: ?line {ok, [Name]} = erl_tar:table(Archive), 98: ?line ok = erl_tar:extract(Archive, [verbose]), 99: 100: %% Verify contents of extracted file. 101: ?line {ok, Bin} = file:read_file(Name), 102: ?line true = match_byte_list(X0, binary_to_list(Bin)), 103: 104: %% Verify that Unix tar can read it. 105: ?line tar_tf(Archive, Name), 106: 107: ok. 108: 109: tar_tf(Archive, Name) -> 110: case os:type() of 111: {unix, _} -> 112: tar_tf1(Archive, Name); 113: _ -> 114: ok 115: end. 116: 117: tar_tf1(Archive, Name) -> 118: ?line Expect = Name ++ "\n", 119: ?line cmd_expect("tar tf " ++ Archive, Expect). 120: 121: %% We can't use os:cmd/1, because Unix 'tar tf Name' on Solaris never 122: %% terminates when given an archive of a size it doesn't like. 123: 124: cmd_expect(Cmd, Expect) -> 125: ?line Port = open_port({spawn, make_cmd(Cmd)}, [stream, in, eof]), 126: ?line get_data(Port, Expect). 127: 128: get_data(Port, Expect) -> 129: receive 130: {Port, {data, Bytes}} -> 131: ?line get_data(Port, match_output(Bytes, Expect, Port)); 132: {Port, eof} -> 133: Port ! {self(), close}, 134: receive 135: {Port, closed} -> 136: true 137: end, 138: receive 139: {'EXIT', Port, _} -> 140: ok 141: after 1 -> % force context switch 142: ok 143: end, 144: ?line match_output(eof, Expect, Port) 145: end. 146: 147: match_output([C|Output], [C|Expect], Port) -> 148: ?line match_output(Output, Expect, Port); 149: match_output([_|_], [_|_], Port) -> 150: ?line kill_port_and_fail(Port, badmatch); 151: match_output([X|Output], [], Port) -> 152: ?line kill_port_and_fail(Port, {too_much_data, [X|Output]}); 153: match_output([], Expect, _Port) -> 154: Expect; 155: match_output(eof, [], _Port) -> 156: []; 157: match_output(eof, _Expect, Port) -> 158: ?line kill_port_and_fail(Port, unexpected_end_of_input). 159: 160: kill_port_and_fail(Port, Reason) -> 161: unlink(Port), 162: exit(Port, die), 163: test_server:fail(Reason). 164: 165: make_cmd(Cmd) -> 166: case os:type() of 167: {win32, _} -> lists:concat(["cmd /c", Cmd]); 168: {unix, _} -> lists:concat(["sh -c '", Cmd, "'"]) 169: end. 170: 171: %% Verifies a random byte list. 172: 173: match_byte_list(X0, [Byte|Rest]) -> 174: X = next_random(X0), 175: case (X bsr 26) band 16#ff of 176: Byte -> match_byte_list(X, Rest); 177: _ -> false 178: end; 179: match_byte_list(_, []) -> 180: true. 181: 182: %% Generates a random byte list. 183: 184: random_byte_list(X0, Count) -> 185: random_byte_list(X0, Count, []). 186: 187: random_byte_list(X0, Count, Result) when Count > 0-> 188: X = next_random(X0), 189: random_byte_list(X, Count-1, [(X bsr 26) band 16#ff|Result]); 190: random_byte_list(_X, 0, Result) -> 191: lists:reverse(Result). 192: 193: %% This RNG is from line 21 on page 102 in Knuth: The Art of Computer Programming, 194: %% Volume II, Seminumerical Algorithms. 195: 196: next_random(X) -> 197: (X*17059465+1) band 16#fffffffff. 198: 199: atomic(doc) -> 200: ["Test the 'atomic' operations: create/extract/table, on compressed " 201: "and uncompressed archives." 202: "Also test the 'cooked' option."]; 203: atomic(suite) -> []; 204: atomic(Config) when is_list(Config) -> 205: ?line ok = file:set_cwd(?config(priv_dir, Config)), 206: ?line DataFiles = data_files(), 207: ?line Names = [Name || {Name,_,_} <- DataFiles], 208: io:format("Names: ~p", [Names]), 209: 210: %% Create an uncompressed archive. The compressed flag should still be 211: %% allowed when listing contents or extracting. 212: 213: ?line Tar1 = "uncompressed.tar", 214: ?line erl_tar:create(Tar1, Names, []), 215: ?line {ok, Names} = erl_tar:table(Tar1, []), 216: ?line {ok, Names} = erl_tar:table(Tar1, [compressed]), 217: ?line {ok, Names} = erl_tar:table(Tar1, [cooked]), 218: ?line {ok, Names} = erl_tar:table(Tar1, [compressed,cooked]), 219: 220: %% Create a compressed archive. 221: 222: ?line Tar2 = "compressed.tar", 223: ?line erl_tar:create(Tar2, Names, [compressed]), 224: ?line {ok, Names} = erl_tar:table(Tar2, [compressed]), 225: ?line {error, Reason} = erl_tar:table(Tar2, []), 226: ?line {ok, Names} = erl_tar:table(Tar2, [compressed,cooked]), 227: ?line {error, Reason} = erl_tar:table(Tar2, [cooked]), 228: ?line ok = io:format("No compressed option: ~p, ~s", 229: [Reason, erl_tar:format_error(Reason)]), 230: 231: %% Same test again, but this time created with 'cooked' 232: 233: ?line Tar3 = "uncompressed_cooked.tar", 234: ?line erl_tar:create(Tar3, Names, [cooked]), 235: ?line {ok, Names} = erl_tar:table(Tar3, []), 236: ?line {ok, Names} = erl_tar:table(Tar3, [compressed]), 237: ?line {ok, Names} = erl_tar:table(Tar3, [cooked]), 238: ?line {ok, Names} = erl_tar:table(Tar3, [compressed,cooked]), 239: 240: ?line Tar4 = "compressed_cooked.tar", 241: ?line erl_tar:create(Tar4, Names, [compressed,cooked]), 242: ?line {ok, Names} = erl_tar:table(Tar4, [compressed]), 243: ?line {error, Reason} = erl_tar:table(Tar4, []), 244: ?line {ok, Names} = erl_tar:table(Tar4, [compressed,cooked]), 245: ?line {error, Reason} = erl_tar:table(Tar4, [cooked]), 246: ?line ok = io:format("No compressed option: ~p, ~s", 247: [Reason, erl_tar:format_error(Reason)]), 248: 249: %% Clean up. 250: ?line delete_files([Tar1,Tar2,Tar3,Tar4|Names]), 251: 252: ok. 253: 254: %% Returns a sequence of characters. 255: 256: char_seq(N, First) -> 257: char_seq(N, First, []). 258: 259: char_seq(0, _, Result) -> 260: Result; 261: char_seq(N, C, Result) when C < 127 -> 262: char_seq(N-1, C+1, [C|Result]); 263: char_seq(N, _, Result) -> 264: char_seq(N, $!, Result). 265: 266: data_files() -> 267: Files = [{"first_file", 1555, $a}, 268: {"small_file", 7, $d}, 269: {"big_file", 23875, $e}, 270: {"last_file", 7500, $g}], 271: create_files(Files), 272: Files. 273: 274: create_files([{Name, Size, First}|Rest]) -> 275: ok = file:write_file(Name, char_seq(Size, First)), 276: create_files(Rest); 277: create_files([]) -> 278: ok. 279: 280: long_names(doc) -> 281: ["Test to extract an Unix tar file containing filenames longer than 100 ", 282: "characters and empty directories."]; 283: long_names(Config) when is_list(Config) -> 284: ?line DataDir = ?config(data_dir, Config), 285: ?line Long = filename:join(DataDir, "long_names.tar"), 286: run_in_short_tempdir(Config, 287: fun() -> do_long_names(Long) end). 288: 289: do_long_names(Long) -> 290: %% Try table/2 and extract/2. 291: ?line case erl_tar:table(Long, [verbose]) of 292: {ok,List} when is_list(List) -> 293: ?line io:format("~p\n", [List]) 294: end, 295: 296: ?line {ok,Cwd} = file:get_cwd(), 297: ?line ok = erl_tar:extract(Long), 298: ?line Base = filename:join([Cwd, "original_software", "written_by", 299: "a_bunch_of_hackers", 300: "spending_all_their_nights", 301: "still", "not_long_enough", 302: "but_soon_it_will_be"]), 303: 304: %% Verify that the empty directory was created. 305: ?line EmptyDir = filename:join(Base, "empty_directory"), 306: ?line {ok, #file_info{type=directory}} = file:read_file_info(EmptyDir), 307: 308: %% Verify that the files were created. 309: ?line {ok,First} = file:read_file(filename:join(Base, "first_file")), 310: ?line {ok,Second} = file:read_file(filename:join(Base, "second_file")), 311: ?line "Here"++_ = binary_to_list(First), 312: ?line "And"++_ = binary_to_list(Second), 313: 314: ok. 315: 316: create_long_names(doc) -> 317: ["Creates a tar file from a deep directory structure (filenames are ", 318: "longer than 100 characters)."]; 319: create_long_names(Config) when is_list(Config) -> 320: run_in_short_tempdir(Config, fun create_long_names/0). 321: 322: create_long_names() -> 323: ?line {ok,Dir} = file:get_cwd(), 324: Dirs = ["aslfjkshjkhliuf", 325: "asdhjfehnbfsky", 326: "sahajfskdfhsz", 327: "asldfkdlfy4y8rchg", 328: "f7nafhjgffagkhsfkhsjk", 329: "dfjasldkfjsdkfjashbv"], 330: 331: ?line DeepDir = make_dirs(Dirs, []), 332: ?line AFile = filename:join(DeepDir, "a_file"), 333: ?line Hello = "hello, world\n", 334: ?line ok = file:write_file(AFile, Hello), 335: ?line TarName = filename:join(Dir, "my_tar_with_long_names.tar"), 336: ?line ok = erl_tar:create(TarName, [AFile]), 337: 338: %% Print contents. 339: ?line ok = erl_tar:tt(TarName), 340: 341: %% Extract and verify. 342: ?line ExtractDir = "extract_dir", 343: ?line ok = file:make_dir(ExtractDir), 344: ?line ok = erl_tar:extract(TarName, [{cwd,ExtractDir}]), 345: ?line {ok, Bin} = file:read_file(filename:join(ExtractDir, AFile)), 346: ?line Hello = binary_to_list(Bin), 347: 348: ok. 349: 350: make_dirs([Dir|Rest], []) -> 351: ?line ok = file:make_dir(Dir), 352: ?line make_dirs(Rest, Dir); 353: make_dirs([Dir|Rest], Parent) -> 354: ?line Name = filename:join(Parent, Dir), 355: ?line ok = file:make_dir(Name), 356: ?line make_dirs(Rest, Name); 357: make_dirs([], Dir) -> 358: Dir. 359: 360: bad_tar(doc) -> 361: ["Try erl_tar:table/2 and erl_tar:extract/2 on some corrupted tar files."]; 362: bad_tar(Config) when is_list(Config) -> 363: ?line try_bad("bad_checksum", bad_header, Config), 364: ?line try_bad("bad_octal", bad_header, Config), 365: ?line try_bad("bad_too_short", eof, Config), 366: ?line try_bad("bad_even_shorter", eof, Config), 367: ok. 368: 369: try_bad(Name0, Reason, Config) -> 370: %% Intentionally no ?line macros here. 371: 372: DataDir = ?config(data_dir, Config), 373: PrivDir = ?config(priv_dir, Config), 374: Name = Name0 ++ ".tar", 375: io:format("~nTrying ~s", [Name]), 376: Full = filename:join(DataDir, Name), 377: Opts = [verbose, {cwd, PrivDir}], 378: Expected = {error, Reason}, 379: case {erl_tar:table(Full, Opts), erl_tar:extract(Full, Opts)} of 380: {Expected, Expected} -> 381: io:format("Result: ~p", [Expected]), 382: case catch erl_tar:format_error(Reason) of 383: {'EXIT', CrashReason} -> 384: test_server:fail({format_error, crashed, CrashReason}); 385: String when is_list(String) -> 386: io:format("format_error(~p) -> ~s", [Reason, String]); 387: Other -> 388: test_server:fail({format_error, returned, Other}) 389: end; 390: {Other1, Other2} -> 391: io:format("table/2 returned ~p", [Other1]), 392: io:format("extract/2 returned ~p", [Other2]), 393: test_server:fail({bad_return_value, Other1, Other2}) 394: end. 395: 396: errors(doc) -> 397: ["Tests that some common errors return correct error codes ", 398: "and that format_error/1 handles them correctly."]; 399: errors(Config) when is_list(Config) -> 400: ?line PrivDir = ?config(priv_dir, Config), 401: 402: %% Give the tar file the same name as a directory. 403: ?line BadTar = filename:join(PrivDir, "bad_tarfile.tar"), 404: ?line ok = file:make_dir(BadTar), 405: ?line try_error(erl_tar, create, [BadTar, []], {BadTar, eisdir}), 406: 407: %% Try including non-existent files in the tar file. 408: ?line NonExistent = "non_existent_file", 409: ?line GoodTar = filename:join(PrivDir, "a_good_tarfile.tar"), 410: ?line try_error(erl_tar, create, [GoodTar, [NonExistent]], 411: {NonExistent, enoent}), 412: 413: %% Clean up. 414: ?line delete_files([GoodTar,BadTar]), 415: 416: ok. 417: 418: try_error(M, F, A, Error) -> 419: io:format("Trying ~p:~p(~p)", [M, F, A]), 420: case catch apply(M, F, A) of 421: {'EXIT', Reason} -> 422: exit(Reason); 423: ok -> 424: test_server:fail(unexpected_success); 425: {error, Error} -> 426: case catch erl_tar:format_error(Error) of 427: {'EXIT', FReason} -> 428: test_server:fail({format_error, crashed, FReason}); 429: String when is_list(String) -> 430: io:format("format_error(~p) -> ~s", [Error, String]); 431: Other -> 432: test_server:fail({format_error, returned, Other}) 433: end; 434: Other -> 435: test_server:fail({expected, {error, Error}, actual, Other}) 436: end. 437: 438: %% remove_prefix(Prefix, List) -> ListWithoutPrefix. 439: 440: remove_prefix([C|Rest1], [C|Rest2]) -> 441: remove_prefix(Rest1, Rest2); 442: remove_prefix(_, Result) -> 443: Result. 444: 445: extract_from_binary(doc) -> 446: "Test extracting a tar archive from a binary."; 447: extract_from_binary(Config) when is_list(Config) -> 448: ?line DataDir = ?config(data_dir, Config), 449: ?line PrivDir = ?config(priv_dir, Config), 450: ?line Long = filename:join(DataDir, "no_fancy_stuff.tar"), 451: ?line ExtractDir = filename:join(PrivDir, "extract_from_binary"), 452: ?line ok = file:make_dir(ExtractDir), 453: 454: %% Read a tar file into a binary and extract from the binary. 455: ?line {ok, Bin} = file:read_file(Long), 456: ?line ok = erl_tar:extract({binary, Bin}, [{cwd,ExtractDir}]), 457: 458: %% Verify. 459: Dir = filename:join(ExtractDir, "no_fancy_stuff"), 460: ?line true = filelib:is_dir(Dir), 461: ?line true = filelib:is_file(filename:join(Dir, "a_dir_list")), 462: ?line true = filelib:is_file(filename:join(Dir, "EPLICENCE")), 463: 464: %% Clean up. 465: ?line delete_files([ExtractDir]), 466: 467: ok. 468: 469: extract_from_binary_compressed(Config) when is_list(Config) -> 470: %% Test extracting a compressed tar archive from a binary. 471: ?line DataDir = ?config(data_dir, Config), 472: ?line PrivDir = ?config(priv_dir, Config), 473: ?line Name = filename:join(DataDir, "cooked_tar_problem.tar.gz"), 474: ?line ExtractDir = filename:join(PrivDir, "extract_from_binary_compressed"), 475: ?line ok = file:make_dir(ExtractDir), 476: ?line {ok,Bin} = file:read_file(Name), 477: 478: %% Try taking contents. 479: ?line {ok,Files} = erl_tar:table({binary,Bin}, [compressed]), 480: ?line io:format("~p\n", [Files]), 481: ?line 19 = length(Files), 482: 483: %% Trying extracting from a binary. 484: ?line ok = erl_tar:extract({binary,Bin}, [compressed,{cwd,ExtractDir}]), 485: ?line {ok,List} = file:list_dir(filename:join(ExtractDir, "ddll_SUITE_data")), 486: ?line io:format("~p\n", [List]), 487: ?line 19 = length(List), 488: 489: %% Clean up while at the same time testing that all file 490: %% were extracted as expected. 491: lists:foreach(fun(N) -> 492: File = filename:join(ExtractDir, N), 493: io:format("Deleting: ~p\n", [File]), 494: ?line ok = file:delete(File) 495: end, Files), 496: 497: %% Clean up the rest. 498: ?line delete_files([ExtractDir]), 499: 500: ok. 501: 502: extract_from_open_file(doc) -> 503: "Test extracting a tar archive from an open file."; 504: extract_from_open_file(Config) when is_list(Config) -> 505: ?line DataDir = ?config(data_dir, Config), 506: ?line PrivDir = ?config(priv_dir, Config), 507: ?line Long = filename:join(DataDir, "no_fancy_stuff.tar"), 508: ?line ExtractDir = filename:join(PrivDir, "extract_from_open_file"), 509: ?line ok = file:make_dir(ExtractDir), 510: 511: ?line {ok, File} = file:open(Long, [read]), 512: ?line ok = erl_tar:extract({file, File}, [{cwd,ExtractDir}]), 513: 514: %% Verify. 515: Dir = filename:join(ExtractDir, "no_fancy_stuff"), 516: ?line true = filelib:is_dir(Dir), 517: ?line true = filelib:is_file(filename:join(Dir, "a_dir_list")), 518: ?line true = filelib:is_file(filename:join(Dir, "EPLICENCE")), 519: 520: %% Close open file. 521: ?line ok = file:close(File), 522: 523: %% Clean up. 524: ?line delete_files([ExtractDir]), 525: 526: ok. 527: 528: symlinks(doc) -> 529: "Test that archives containing symlinks can be created and extracted."; 530: symlinks(Config) when is_list(Config) -> 531: ?line PrivDir = ?config(priv_dir, Config), 532: ?line Dir = filename:join(PrivDir, "symlinks"), 533: ?line ok = file:make_dir(Dir), 534: ?line ABadSymlink = filename:join(Dir, "bad_symlink"), 535: ?line PointsTo = "/a/definitely/non_existing/path", 536: ?line Res = case make_symlink("/a/definitely/non_existing/path", ABadSymlink) of 537: {error, enotsup} -> 538: {skip, "Symbolic links not supported on this platform"}; 539: ok -> 540: symlinks(Dir, "bad_symlink", PointsTo), 541: long_symlink(Dir) 542: end, 543: 544: %% Clean up. 545: ?line delete_files([Dir]), 546: Res. 547: 548: make_symlink(Path, Link) -> 549: case os:type() of 550: {win32,_} -> 551: %% Symlinks on Windows have two problems: 552: %% 1) file:read_link_info/1 cannot read out the target 553: %% of the symlink if the target does not exist. 554: %% That is possible (but not easy) to fix in the 555: %% efile driver. 556: %% 557: %% 2) Symlinks to files and directories are different 558: %% creatures. If the target is not existing, the 559: %% symlink will be created to be of the file-pointing 560: %% type. That can be partially worked around in erl_tar 561: %% by creating all symlinks when the end of the tar 562: %% file has been reached. 563: %% 564: %% But for now, pretend that there are no symlinks on 565: %% Windows. 566: {error, enotsup}; 567: _ -> 568: file:make_symlink(Path, Link) 569: end. 570: 571: symlinks(Dir, BadSymlink, PointsTo) -> 572: ?line Tar = filename:join(Dir, "symlink.tar"), 573: ?line DerefTar = filename:join(Dir, "dereference.tar"), 574: 575: %% Create the archive. 576: 577: ?line ok = file:set_cwd(Dir), 578: ?line GoodSymlink = "good_symlink", 579: ?line AFile = "a_good_file", 580: ?line ALine = "A line of text for a file.", 581: ?line ok = file:write_file(AFile, ALine), 582: ?line ok = file:make_symlink(AFile, GoodSymlink), 583: ?line ok = erl_tar:create(Tar, [BadSymlink, GoodSymlink, AFile], [verbose]), 584: 585: %% List contents of tar file. 586: 587: ?line ok = erl_tar:tt(Tar), 588: 589: %% Also create another archive with the dereference flag. 590: 591: ?line ok = erl_tar:create(DerefTar, [AFile, GoodSymlink], [dereference, verbose]), 592: 593: %% Extract files to a new directory. 594: 595: ?line NewDir = filename:join(Dir, "extracted"), 596: ?line ok = file:make_dir(NewDir), 597: ?line ok = erl_tar:extract(Tar, [{cwd, NewDir}, verbose]), 598: 599: %% Verify that the files are there. 600: 601: ?line ok = file:set_cwd(NewDir), 602: ?line {ok, #file_info{type=symlink}} = file:read_link_info(BadSymlink), 603: ?line {ok, PointsTo} = file:read_link(BadSymlink), 604: ?line {ok, #file_info{type=symlink}} = file:read_link_info(GoodSymlink), 605: ?line {ok, AFile} = file:read_link(GoodSymlink), 606: ?line Expected = list_to_binary(ALine), 607: ?line {ok, Expected} = file:read_file(GoodSymlink), 608: 609: %% Extract the "dereferenced archive" to a new directory. 610: 611: ?line NewDirDeref = filename:join(Dir, "extracted_deref"), 612: ?line ok = file:make_dir(NewDirDeref), 613: ?line ok = erl_tar:extract(DerefTar, [{cwd, NewDirDeref}, verbose]), 614: 615: %% Verify that the files are there. 616: 617: ?line ok = file:set_cwd(NewDirDeref), 618: ?line {ok, #file_info{type=regular}} = file:read_link_info(GoodSymlink), 619: ?line {ok, #file_info{type=regular}} = file:read_link_info(AFile), 620: ?line {ok, Expected} = file:read_file(GoodSymlink), 621: ?line {ok, Expected} = file:read_file(AFile), 622: 623: ok. 624: 625: long_symlink(Dir) -> 626: ?line Tar = filename:join(Dir, "long_symlink.tar"), 627: ?line ok = file:set_cwd(Dir), 628: 629: ?line AFile = "long_symlink", 630: ?line FarTooLong = "/tmp/aarrghh/this/path/is/far/longer/than/one/hundred/characters/which/is/the/maximum/number/of/characters/allowed", 631: ?line ok = file:make_symlink(FarTooLong, AFile), 632: ?line {error,Error} = erl_tar:create(Tar, [AFile], [verbose]), 633: ?line io:format("Error: ~s\n", [erl_tar:format_error(Error)]), 634: ?line {FarTooLong,symbolic_link_too_long} = Error, 635: ok. 636: 637: open_add_close(Config) when is_list(Config) -> 638: ?line PrivDir = ?config(priv_dir, Config), 639: ?line ok = file:set_cwd(PrivDir), 640: ?line Dir = filename:join(PrivDir, "open_add_close"), 641: ?line ok = file:make_dir(Dir), 642: 643: ?line [{FileOne,_,_},{FileTwo,_,_},{FileThree,_,_}] = oac_files(), 644: ?line ADir = "empty_dir", 645: ?line AnotherDir = "another_dir", 646: ?line SomeContent = filename:join(AnotherDir, "some_content"), 647: ?line ok = file:make_dir(ADir), 648: ?line ok = file:make_dir(AnotherDir), 649: ?line ok = file:make_dir(SomeContent), 650: 651: ?line TarOne = filename:join(Dir, "archive1.tar"), 652: ?line {ok,AD} = erl_tar:open(TarOne, [write]), 653: ?line ok = erl_tar:add(AD, FileOne, []), 654: ?line ok = erl_tar:add(AD, FileTwo, "second file", []), 655: ?line ok = erl_tar:add(AD, FileThree, [verbose]), 656: ?line ok = erl_tar:add(AD, ADir, [verbose]), 657: ?line ok = erl_tar:add(AD, AnotherDir, [verbose]), 658: ?line ok = erl_tar:close(AD), 659: 660: ?line ok = erl_tar:t(TarOne), 661: ?line ok = erl_tar:tt(TarOne), 662: 663: ?line {ok,[FileOne,"second file",FileThree,ADir,SomeContent]} = erl_tar:table(TarOne), 664: 665: ?line delete_files(["oac_file","oac_small","oac_big",Dir,AnotherDir,ADir]), 666: 667: ok. 668: 669: oac_files() -> 670: Files = [{"oac_file", 1459, $x}, 671: {"oac_small", 99, $w}, 672: {"oac_big", 33896, $A}], 673: create_files(Files), 674: Files. 675: 676: cooked_compressed(Config) when is_list(Config) -> 677: %% Test that a compressed archive can be read in cooked mode. 678: ?line DataDir = ?config(data_dir, Config), 679: ?line PrivDir = ?config(priv_dir, Config), 680: ?line Name = filename:join(DataDir, "cooked_tar_problem.tar.gz"), 681: 682: %% Try table/2 and extract/2. 683: ?line {ok,List} = erl_tar:table(Name, [cooked,compressed]), 684: ?line io:format("~p\n", [List]), 685: ?line 19 = length(List), 686: ?line ok = erl_tar:extract(Name, [cooked,compressed,{cwd,PrivDir}]), 687: 688: %% Clean up while at the same time testing that all file 689: %% were extracted as expected. 690: lists:foreach(fun(N) -> 691: File = filename:join(PrivDir, N), 692: io:format("Deleting: ~p\n", [File]), 693: ?line ok = file:delete(File) 694: end, List), 695: 696: %% Clean up. 697: ?line delete_files([filename:join(PrivDir, "ddll_SUITE_data")]), 698: ok. 699: 700: memory(doc) -> 701: ["Test that an archive can be created directly from binaries and " 702: "that an archive can be extracted into binaries."]; 703: memory(Config) when is_list(Config) -> 704: ?line DataDir = ?config(data_dir, Config), 705: 706: ?line FileBins = [{"bar/fum", <<"BARFUM">>},{"foo", <<"FOO">>}], 707: ?line Name1 = filename:join(DataDir, "memory.tar"), 708: ?line ok = erl_tar:create(Name1, FileBins, [write,verbose]), 709: ?line {ok,Extracted1} = erl_tar:extract(Name1, [memory,verbose]), 710: ?line FileBins1 = lists:sort(Extracted1), 711: 712: ?line io:format("FileBins: ~p\n", [FileBins]), 713: ?line io:format("FileBins1: ~p\n", [FileBins1]), 714: ?line FileBins = FileBins1, 715: 716: ?line Name2 = filename:join(DataDir, "memory2.tar"), 717: ?line {ok,Fd} = erl_tar:open(Name2, [write]), 718: ?line [ok,ok] = [erl_tar:add(Fd, B, N, [write,verbose]) || {N,B} <- FileBins], 719: ?line ok = erl_tar:close(Fd), 720: ?line {ok,Extracted2} = erl_tar:extract(Name2, [memory,verbose]), 721: ?line FileBins2 = lists:sort(Extracted2), 722: ?line io:format("FileBins2: ~p\n", [FileBins2]), 723: ?line FileBins = FileBins2, 724: 725: %% Clean up. 726: ?line ok = delete_files([Name1,Name2]), 727: ok. 728: 729: %% Delete the given list of files. 730: delete_files([]) -> ok; 731: delete_files([Item|Rest]) -> 732: case file:delete(Item) of 733: ok -> 734: delete_files(Rest); 735: {error,eperm} -> 736: file:change_mode(Item, 8#777), 737: delete_files(filelib:wildcard(filename:join(Item, "*"))), 738: file:del_dir(Item), 739: ok; 740: {error,eacces} -> 741: %% We'll see about that! 742: file:change_mode(Item, 8#777), 743: case file:delete(Item) of 744: ok -> ok; 745: {error,_} -> 746: erlang:yield(), 747: file:change_mode(Item, 8#777), 748: file:delete(Item), 749: ok 750: end; 751: {error,_} -> ok 752: end, 753: delete_files(Rest). 754: 755: %% Move to a temporary directory with as short name as possible and 756: %% execute Fun. Remove the directory and any files in it afterwards. 757: %% This is necessary because pathnames on Windows may be limited to 758: %% 260 characters. 759: run_in_short_tempdir(Config, Fun) -> 760: {ok,Cwd} = file:get_cwd(), 761: PrivDir0 = ?config(priv_dir, Config), 762: 763: %% Normalize name to make sure that there is no slash at the end. 764: PrivDir = filename:absname(PrivDir0), 765: 766: %% We need a base directory with a much shorter pathname than 767: %% priv_dir. We KNOW that priv_dir is located four levels below 768: %% the directory that common_test puts the ct_run.* directories 769: %% in. That fact is not documented, but a usually reliable source 770: %% assured me that the directory structure is unlikely to change 771: %% in future versions of common_test because of backwards 772: %% compatibility (tools developed by users of common_test depend 773: %% on the current directory layout). 774: Base = lists:foldl(fun(_, D) -> 775: filename:dirname(D) 776: end, PrivDir, [1,2,3,4]), 777: 778: Dir = make_temp_dir(Base, 0), 779: ok = file:set_cwd(Dir), 780: io:format("Running test in ~s\n", [Dir]), 781: try 782: Fun() 783: after 784: file:set_cwd(Cwd), 785: delete_files([Dir]) 786: end. 787: 788: make_temp_dir(Base, I) -> 789: Name = filename:join(Base, integer_to_list(I, 36)), 790: case file:make_dir(Name) of 791: ok -> Name; 792: {error,eexist} -> make_temp_dir(Base, I+1) 793: end.