1 /*-
2 * Copyright (c) 2003-2007 Tim Kientzle
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25 #include "test.h"
26
27 #if defined(_WIN32) && !defined(__CYGWIN__)
28 /* Execution bits, Group members bits and others bits do not work. */
29 #define UMASK 0177
30 #define E_MASK (~0177)
31 #else
32 #define UMASK 022
33 #define E_MASK (~0)
34 #endif
35
36 /*
37 * Exercise hardlink recreation.
38 *
39 * File permissions are chosen so that the authoritative entry
40 * has the correct permission and the non-authoritative versions
41 * are just writeable files.
42 */
DEFINE_TEST(test_write_disk_hardlink)43 DEFINE_TEST(test_write_disk_hardlink)
44 {
45 #if defined(__HAIKU__)
46 skipping("archive_write_disk_hardlink; hardlinks are not supported on bfs");
47 #else
48 static const char data[]="abcdefghijklmnopqrstuvwxyz";
49 struct archive *ad;
50 struct archive_entry *ae;
51 #ifdef HAVE_LINKAT
52 int can_symlink;
53 #endif
54 int r;
55
56 /* Force the umask to something predictable. */
57 assertUmask(UMASK);
58
59 /* Write entries to disk. */
60 assert((ad = archive_write_disk_new()) != NULL);
61
62 /*
63 * First, use a tar-like approach; a regular file, then
64 * a separate "hardlink" entry.
65 */
66
67 /* Regular file. */
68 assert((ae = archive_entry_new()) != NULL);
69 archive_entry_copy_pathname(ae, "link1a");
70 archive_entry_set_mode(ae, S_IFREG | 0755);
71 archive_entry_set_size(ae, sizeof(data));
72 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
73 assertEqualInt(sizeof(data),
74 archive_write_data(ad, data, sizeof(data)));
75 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
76 archive_entry_free(ae);
77
78 /* Link. Size of zero means this doesn't carry data. */
79 assert((ae = archive_entry_new()) != NULL);
80 archive_entry_copy_pathname(ae, "link1b");
81 archive_entry_set_mode(ae, S_IFREG | 0642);
82 archive_entry_set_size(ae, 0);
83 archive_entry_copy_hardlink(ae, "link1a");
84 assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
85 if (r >= ARCHIVE_WARN) {
86 assertEqualInt(ARCHIVE_WARN,
87 archive_write_data(ad, data, sizeof(data)));
88 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
89 }
90 archive_entry_free(ae);
91
92 /*
93 * Repeat tar approach test, but use unset to mark the
94 * hardlink as having no data.
95 */
96
97 /* Regular file. */
98 assert((ae = archive_entry_new()) != NULL);
99 archive_entry_copy_pathname(ae, "link2a");
100 archive_entry_set_mode(ae, S_IFREG | 0755);
101 archive_entry_set_size(ae, sizeof(data));
102 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
103 assertEqualInt(sizeof(data),
104 archive_write_data(ad, data, sizeof(data)));
105 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
106 archive_entry_free(ae);
107
108 /* Link. Unset size means this doesn't carry data. */
109 assert((ae = archive_entry_new()) != NULL);
110 archive_entry_copy_pathname(ae, "link2b");
111 archive_entry_set_mode(ae, S_IFREG | 0642);
112 archive_entry_unset_size(ae);
113 archive_entry_copy_hardlink(ae, "link2a");
114 assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
115 if (r >= ARCHIVE_WARN) {
116 assertEqualInt(ARCHIVE_WARN,
117 archive_write_data(ad, data, sizeof(data)));
118 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
119 }
120 archive_entry_free(ae);
121
122 /*
123 * Second, try an old-cpio-like approach; a regular file, then
124 * another identical one (which has been marked hardlink).
125 */
126
127 /* Regular file. */
128 assert((ae = archive_entry_new()) != NULL);
129 archive_entry_copy_pathname(ae, "link3a");
130 archive_entry_set_mode(ae, S_IFREG | 0600);
131 archive_entry_set_size(ae, sizeof(data));
132 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
133 assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
134 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
135 archive_entry_free(ae);
136
137 /* Link. */
138 assert((ae = archive_entry_new()) != NULL);
139 archive_entry_copy_pathname(ae, "link3b");
140 archive_entry_set_mode(ae, S_IFREG | 0755);
141 archive_entry_set_size(ae, sizeof(data));
142 archive_entry_copy_hardlink(ae, "link3a");
143 assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
144 if (r > ARCHIVE_WARN) {
145 assertEqualInt(sizeof(data),
146 archive_write_data(ad, data, sizeof(data)));
147 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
148 }
149 archive_entry_free(ae);
150
151 /*
152 * Third, try a new-cpio-like approach, where the initial
153 * regular file is empty and the hardlink has the data.
154 */
155
156 /* Regular file. */
157 assert((ae = archive_entry_new()) != NULL);
158 archive_entry_copy_pathname(ae, "link4a");
159 archive_entry_set_mode(ae, S_IFREG | 0600);
160 archive_entry_set_size(ae, 0);
161 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
162 assertEqualInt(ARCHIVE_WARN, archive_write_data(ad, data, 1));
163 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
164 archive_entry_free(ae);
165
166 /* Link. */
167 assert((ae = archive_entry_new()) != NULL);
168 archive_entry_copy_pathname(ae, "link4b");
169 archive_entry_set_mode(ae, S_IFREG | 0755);
170 archive_entry_set_size(ae, sizeof(data));
171 archive_entry_copy_hardlink(ae, "link4a");
172 assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
173 if (r > ARCHIVE_FAILED) {
174 assertEqualInt(sizeof(data),
175 archive_write_data(ad, data, sizeof(data)));
176 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
177 }
178 archive_entry_free(ae);
179
180 #ifdef HAVE_LINKAT
181 /* Finally, try creating a hard link to a dangling symlink */
182 can_symlink = canSymlink();
183 if (can_symlink) {
184 /* Symbolic link: link5a -> foo */
185 assert((ae = archive_entry_new()) != NULL);
186 archive_entry_copy_pathname(ae, "link5a");
187 archive_entry_set_mode(ae, AE_IFLNK | 0642);
188 archive_entry_unset_size(ae);
189 archive_entry_copy_symlink(ae, "foo");
190 assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
191 if (r >= ARCHIVE_WARN) {
192 assertEqualInt(ARCHIVE_WARN,
193 archive_write_data(ad, data, sizeof(data)));
194 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
195 }
196 archive_entry_free(ae);
197
198
199 /* Link. Size of zero means this doesn't carry data. */
200 assert((ae = archive_entry_new()) != NULL);
201 archive_entry_copy_pathname(ae, "link5b");
202 archive_entry_set_mode(ae, S_IFREG | 0642);
203 archive_entry_set_size(ae, 0);
204 archive_entry_copy_hardlink(ae, "link5a");
205 assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
206 if (r >= ARCHIVE_WARN) {
207 assertEqualInt(ARCHIVE_WARN,
208 archive_write_data(ad, data, sizeof(data)));
209 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
210 }
211 archive_entry_free(ae);
212 }
213 #endif
214 assertEqualInt(0, archive_write_free(ad));
215
216 /* Test the entries on disk. */
217
218 /* Test #1 */
219 /* If the hardlink was successfully created and the archive
220 * doesn't carry data for it, we consider it to be
221 * non-authoritative for meta data as well. This is consistent
222 * with GNU tar and BSD pax. */
223 assertIsReg("link1a", 0755 & ~UMASK);
224 assertFileSize("link1a", sizeof(data));
225 assertFileNLinks("link1a", 2);
226 assertIsHardlink("link1a", "link1b");
227
228 /* Test #2: Should produce identical results to test #1 */
229 /* Note that marking a hardlink with size = 0 is treated the
230 * same as having an unset size. This is partly for backwards
231 * compatibility (we used to not have unset tracking, so
232 * relied on size == 0) and partly to match the model used by
233 * common file formats that store a size of zero for
234 * hardlinks. */
235 assertIsReg("link2a", 0755 & ~UMASK);
236 assertFileSize("link2a", sizeof(data));
237 assertFileNLinks("link2a", 2);
238 assertIsHardlink("link2a", "link2b");
239
240 /* Test #3 */
241 assertIsReg("link3a", 0755 & ~UMASK);
242 assertFileSize("link3a", sizeof(data));
243 assertFileNLinks("link3a", 2);
244 assertIsHardlink("link3a", "link3b");
245
246 /* Test #4 */
247 assertIsReg("link4a", 0755 & ~UMASK);
248 assertFileNLinks("link4a", 2);
249 assertFileSize("link4a", sizeof(data));
250 assertIsHardlink("link4a", "link4b");
251
252 #ifdef HAVE_LINKAT
253 if (can_symlink) {
254 /* Test #5 */
255 assertIsSymlink("link5a", "foo", 0);
256 assertFileNLinks("link5a", 2);
257 assertIsHardlink("link5a", "link5b");
258 }
259 #endif
260 #endif
261 }
262