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