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: stable/11/contrib/libarchive/libarchive/test/test_write_disk_hardlink.c 370535 2021-09-10 08:34:36Z git2svn $");
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 */
44DEFINE_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