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 authoritive entry
41 * has the correct permission and the non-authoritive 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	int r;
53
54	/* Force the umask to something predictable. */
55	assertUmask(UMASK);
56
57	/* Write entries to disk. */
58	assert((ad = archive_write_disk_new()) != NULL);
59
60	/*
61	 * First, use a tar-like approach; a regular file, then
62	 * a separate "hardlink" entry.
63	 */
64
65	/* Regular file. */
66	assert((ae = archive_entry_new()) != NULL);
67	archive_entry_copy_pathname(ae, "link1a");
68	archive_entry_set_mode(ae, S_IFREG | 0755);
69	archive_entry_set_size(ae, sizeof(data));
70	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
71	assertEqualInt(sizeof(data),
72	    archive_write_data(ad, data, sizeof(data)));
73	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
74	archive_entry_free(ae);
75
76	/* Link.  Size of zero means this doesn't carry data. */
77	assert((ae = archive_entry_new()) != NULL);
78	archive_entry_copy_pathname(ae, "link1b");
79	archive_entry_set_mode(ae, S_IFREG | 0642);
80	archive_entry_set_size(ae, 0);
81	archive_entry_copy_hardlink(ae, "link1a");
82	assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
83	if (r >= ARCHIVE_WARN) {
84		assertEqualInt(ARCHIVE_WARN,
85		    archive_write_data(ad, data, sizeof(data)));
86		assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
87	}
88	archive_entry_free(ae);
89
90	/*
91	 * Repeat tar approach test, but use unset to mark the
92	 * hardlink as having no data.
93	 */
94
95	/* Regular file. */
96	assert((ae = archive_entry_new()) != NULL);
97	archive_entry_copy_pathname(ae, "link2a");
98	archive_entry_set_mode(ae, S_IFREG | 0755);
99	archive_entry_set_size(ae, sizeof(data));
100	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
101	assertEqualInt(sizeof(data),
102	    archive_write_data(ad, data, sizeof(data)));
103	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
104	archive_entry_free(ae);
105
106	/* Link.  Unset size means this doesn't carry data. */
107	assert((ae = archive_entry_new()) != NULL);
108	archive_entry_copy_pathname(ae, "link2b");
109	archive_entry_set_mode(ae, S_IFREG | 0642);
110	archive_entry_unset_size(ae);
111	archive_entry_copy_hardlink(ae, "link2a");
112	assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
113	if (r >= ARCHIVE_WARN) {
114		assertEqualInt(ARCHIVE_WARN,
115		    archive_write_data(ad, data, sizeof(data)));
116		assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
117	}
118	archive_entry_free(ae);
119
120	/*
121	 * Second, try an old-cpio-like approach; a regular file, then
122	 * another identical one (which has been marked hardlink).
123	 */
124
125	/* Regular file. */
126	assert((ae = archive_entry_new()) != NULL);
127	archive_entry_copy_pathname(ae, "link3a");
128	archive_entry_set_mode(ae, S_IFREG | 0600);
129	archive_entry_set_size(ae, sizeof(data));
130	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
131	assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
132	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
133	archive_entry_free(ae);
134
135	/* Link. */
136	assert((ae = archive_entry_new()) != NULL);
137	archive_entry_copy_pathname(ae, "link3b");
138	archive_entry_set_mode(ae, S_IFREG | 0755);
139	archive_entry_set_size(ae, sizeof(data));
140	archive_entry_copy_hardlink(ae, "link3a");
141	assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
142	if (r > ARCHIVE_WARN) {
143		assertEqualInt(sizeof(data),
144		    archive_write_data(ad, data, sizeof(data)));
145		assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
146	}
147	archive_entry_free(ae);
148
149	/*
150	 * Finally, try a new-cpio-like approach, where the initial
151	 * regular file is empty and the hardlink has the data.
152	 */
153
154	/* Regular file. */
155	assert((ae = archive_entry_new()) != NULL);
156	archive_entry_copy_pathname(ae, "link4a");
157	archive_entry_set_mode(ae, S_IFREG | 0600);
158	archive_entry_set_size(ae, 0);
159	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
160#if ARCHIVE_VERSION_NUMBER < 3000000
161	assertEqualInt(ARCHIVE_WARN, archive_write_data(ad, data, 1));
162#else
163	assertEqualInt(-1, archive_write_data(ad, data, 1));
164#endif
165	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
166	archive_entry_free(ae);
167
168	/* Link. */
169	assert((ae = archive_entry_new()) != NULL);
170	archive_entry_copy_pathname(ae, "link4b");
171	archive_entry_set_mode(ae, S_IFREG | 0755);
172	archive_entry_set_size(ae, sizeof(data));
173	archive_entry_copy_hardlink(ae, "link4a");
174	assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
175	if (r > ARCHIVE_FAILED) {
176		assertEqualInt(sizeof(data),
177		    archive_write_data(ad, data, sizeof(data)));
178		assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
179	}
180	archive_entry_free(ae);
181	assertEqualInt(0, archive_write_finish(ad));
182
183	/* Test the entries on disk. */
184
185	/* Test #1 */
186	/* If the hardlink was successfully created and the archive
187	 * doesn't carry data for it, we consider it to be
188	 * non-authoritive for meta data as well.  This is consistent
189	 * with GNU tar and BSD pax.  */
190	assertIsReg("link1a", 0755 & ~UMASK);
191	assertFileSize("link1a", sizeof(data));
192	assertFileNLinks("link1a", 2);
193	assertIsHardlink("link1a", "link1b");
194
195	/* Test #2: Should produce identical results to test #1 */
196	/* Note that marking a hardlink with size = 0 is treated the
197	 * same as having an unset size.  This is partly for backwards
198	 * compatibility (we used to not have unset tracking, so
199	 * relied on size == 0) and partly to match the model used by
200	 * common file formats that store a size of zero for
201	 * hardlinks. */
202	assertIsReg("link2a", 0755 & ~UMASK);
203	assertFileSize("link2a", sizeof(data));
204	assertFileNLinks("link2a", 2);
205	assertIsHardlink("link2a", "link2b");
206
207	/* Test #3 */
208	assertIsReg("link3a", 0755 & ~UMASK);
209	assertFileSize("link3a", sizeof(data));
210	assertFileNLinks("link3a", 2);
211	assertIsHardlink("link3a", "link3b");
212
213	/* Test #4 */
214	assertIsReg("link4a", 0755 & ~UMASK);
215	assertFileNLinks("link4a", 2);
216	assertFileSize("link4a", sizeof(data));
217	assertIsHardlink("link4a", "link4b");
218#endif
219}
220