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 __FBSDID("$FreeBSD$");
27
28 #if defined(_WIN32) && !defined(__CYGWIN__)
29 /* Execution bits, Group members bits and others bits do not work. */
30 #define UMASK 0177
31 #define E_MASK (~0177)
32 #else
33 #define UMASK 022
34 #define E_MASK (~0)
35 #endif
36
37 /*
38 * Exercise hardlink recreation.
39 *
40 * File permissions are chosen so that the authoritative entry
41 * has the correct permission and the non-authoritative versions
42 * are just writeable files.
43 */
DEFINE_TEST(test_write_disk_hardlink)44 DEFINE_TEST(test_write_disk_hardlink)
45 {
46 #if defined(__HAIKU__)
47 skipping("archive_write_disk_hardlink; hardlinks are not supported on bfs");
48 #else
49 static const char data[]="abcdefghijklmnopqrstuvwxyz";
50 struct archive *ad;
51 struct archive_entry *ae;
52 #ifdef HAVE_LINKAT
53 int can_symlink;
54 #endif
55 int r;
56
57 /* Force the umask to something predictable. */
58 assertUmask(UMASK);
59
60 /* Write entries to disk. */
61 assert((ad = archive_write_disk_new()) != NULL);
62
63 /*
64 * First, use a tar-like approach; a regular file, then
65 * a separate "hardlink" entry.
66 */
67
68 /* Regular file. */
69 assert((ae = archive_entry_new()) != NULL);
70 archive_entry_copy_pathname(ae, "link1a");
71 archive_entry_set_mode(ae, S_IFREG | 0755);
72 archive_entry_set_size(ae, sizeof(data));
73 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
74 assertEqualInt(sizeof(data),
75 archive_write_data(ad, data, sizeof(data)));
76 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
77 archive_entry_free(ae);
78
79 /* Link. Size of zero means this doesn't carry data. */
80 assert((ae = archive_entry_new()) != NULL);
81 archive_entry_copy_pathname(ae, "link1b");
82 archive_entry_set_mode(ae, S_IFREG | 0642);
83 archive_entry_set_size(ae, 0);
84 archive_entry_copy_hardlink(ae, "link1a");
85 assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
86 if (r >= ARCHIVE_WARN) {
87 assertEqualInt(ARCHIVE_WARN,
88 archive_write_data(ad, data, sizeof(data)));
89 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
90 }
91 archive_entry_free(ae);
92
93 /*
94 * Repeat tar approach test, but use unset to mark the
95 * hardlink as having no data.
96 */
97
98 /* Regular file. */
99 assert((ae = archive_entry_new()) != NULL);
100 archive_entry_copy_pathname(ae, "link2a");
101 archive_entry_set_mode(ae, S_IFREG | 0755);
102 archive_entry_set_size(ae, sizeof(data));
103 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
104 assertEqualInt(sizeof(data),
105 archive_write_data(ad, data, sizeof(data)));
106 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
107 archive_entry_free(ae);
108
109 /* Link. Unset size means this doesn't carry data. */
110 assert((ae = archive_entry_new()) != NULL);
111 archive_entry_copy_pathname(ae, "link2b");
112 archive_entry_set_mode(ae, S_IFREG | 0642);
113 archive_entry_unset_size(ae);
114 archive_entry_copy_hardlink(ae, "link2a");
115 assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
116 if (r >= ARCHIVE_WARN) {
117 assertEqualInt(ARCHIVE_WARN,
118 archive_write_data(ad, data, sizeof(data)));
119 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
120 }
121 archive_entry_free(ae);
122
123 /*
124 * Second, try an old-cpio-like approach; a regular file, then
125 * another identical one (which has been marked hardlink).
126 */
127
128 /* Regular file. */
129 assert((ae = archive_entry_new()) != NULL);
130 archive_entry_copy_pathname(ae, "link3a");
131 archive_entry_set_mode(ae, S_IFREG | 0600);
132 archive_entry_set_size(ae, sizeof(data));
133 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
134 assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
135 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
136 archive_entry_free(ae);
137
138 /* Link. */
139 assert((ae = archive_entry_new()) != NULL);
140 archive_entry_copy_pathname(ae, "link3b");
141 archive_entry_set_mode(ae, S_IFREG | 0755);
142 archive_entry_set_size(ae, sizeof(data));
143 archive_entry_copy_hardlink(ae, "link3a");
144 assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
145 if (r > ARCHIVE_WARN) {
146 assertEqualInt(sizeof(data),
147 archive_write_data(ad, data, sizeof(data)));
148 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
149 }
150 archive_entry_free(ae);
151
152 /*
153 * Third, try a new-cpio-like approach, where the initial
154 * regular file is empty and the hardlink has the data.
155 */
156
157 /* Regular file. */
158 assert((ae = archive_entry_new()) != NULL);
159 archive_entry_copy_pathname(ae, "link4a");
160 archive_entry_set_mode(ae, S_IFREG | 0600);
161 archive_entry_set_size(ae, 0);
162 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
163 assertEqualInt(ARCHIVE_WARN, archive_write_data(ad, data, 1));
164 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
165 archive_entry_free(ae);
166
167 /* Link. */
168 assert((ae = archive_entry_new()) != NULL);
169 archive_entry_copy_pathname(ae, "link4b");
170 archive_entry_set_mode(ae, S_IFREG | 0755);
171 archive_entry_set_size(ae, sizeof(data));
172 archive_entry_copy_hardlink(ae, "link4a");
173 assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
174 if (r > ARCHIVE_FAILED) {
175 assertEqualInt(sizeof(data),
176 archive_write_data(ad, data, sizeof(data)));
177 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
178 }
179 archive_entry_free(ae);
180
181 #ifdef HAVE_LINKAT
182 /* Finally, try creating a hard link to a dangling symlink */
183 can_symlink = canSymlink();
184 if (can_symlink) {
185 /* Symbolic link: link5a -> foo */
186 assert((ae = archive_entry_new()) != NULL);
187 archive_entry_copy_pathname(ae, "link5a");
188 archive_entry_set_mode(ae, AE_IFLNK | 0642);
189 archive_entry_unset_size(ae);
190 archive_entry_copy_symlink(ae, "foo");
191 assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
192 if (r >= ARCHIVE_WARN) {
193 assertEqualInt(ARCHIVE_WARN,
194 archive_write_data(ad, data, sizeof(data)));
195 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
196 }
197 archive_entry_free(ae);
198
199
200 /* Link. Size of zero means this doesn't carry data. */
201 assert((ae = archive_entry_new()) != NULL);
202 archive_entry_copy_pathname(ae, "link5b");
203 archive_entry_set_mode(ae, S_IFREG | 0642);
204 archive_entry_set_size(ae, 0);
205 archive_entry_copy_hardlink(ae, "link5a");
206 assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
207 if (r >= ARCHIVE_WARN) {
208 assertEqualInt(ARCHIVE_WARN,
209 archive_write_data(ad, data, sizeof(data)));
210 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
211 }
212 archive_entry_free(ae);
213 }
214 #endif
215 assertEqualInt(0, archive_write_free(ad));
216
217 /* Test the entries on disk. */
218
219 /* Test #1 */
220 /* If the hardlink was successfully created and the archive
221 * doesn't carry data for it, we consider it to be
222 * non-authoritative for meta data as well. This is consistent
223 * with GNU tar and BSD pax. */
224 assertIsReg("link1a", 0755 & ~UMASK);
225 assertFileSize("link1a", sizeof(data));
226 assertFileNLinks("link1a", 2);
227 assertIsHardlink("link1a", "link1b");
228
229 /* Test #2: Should produce identical results to test #1 */
230 /* Note that marking a hardlink with size = 0 is treated the
231 * same as having an unset size. This is partly for backwards
232 * compatibility (we used to not have unset tracking, so
233 * relied on size == 0) and partly to match the model used by
234 * common file formats that store a size of zero for
235 * hardlinks. */
236 assertIsReg("link2a", 0755 & ~UMASK);
237 assertFileSize("link2a", sizeof(data));
238 assertFileNLinks("link2a", 2);
239 assertIsHardlink("link2a", "link2b");
240
241 /* Test #3 */
242 assertIsReg("link3a", 0755 & ~UMASK);
243 assertFileSize("link3a", sizeof(data));
244 assertFileNLinks("link3a", 2);
245 assertIsHardlink("link3a", "link3b");
246
247 /* Test #4 */
248 assertIsReg("link4a", 0755 & ~UMASK);
249 assertFileNLinks("link4a", 2);
250 assertFileSize("link4a", sizeof(data));
251 assertIsHardlink("link4a", "link4b");
252
253 #ifdef HAVE_LINKAT
254 if (can_symlink) {
255 /* Test #5 */
256 assertIsSymlink("link5a", "foo", 0);
257 assertFileNLinks("link5a", 2);
258 assertIsHardlink("link5a", "link5b");
259 }
260 #endif
261 #endif
262 }
263