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  */
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