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 	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 	assertEqualInt(ARCHIVE_WARN, archive_write_data(ad, data, 1));
161 	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
162 	archive_entry_free(ae);
163 
164 	/* Link. */
165 	assert((ae = archive_entry_new()) != NULL);
166 	archive_entry_copy_pathname(ae, "link4b");
167 	archive_entry_set_mode(ae, S_IFREG | 0755);
168 	archive_entry_set_size(ae, sizeof(data));
169 	archive_entry_copy_hardlink(ae, "link4a");
170 	assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
171 	if (r > ARCHIVE_FAILED) {
172 		assertEqualInt(sizeof(data),
173 		    archive_write_data(ad, data, sizeof(data)));
174 		assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
175 	}
176 	archive_entry_free(ae);
177 	assertEqualInt(0, archive_write_free(ad));
178 
179 	/* Test the entries on disk. */
180 
181 	/* Test #1 */
182 	/* If the hardlink was successfully created and the archive
183 	 * doesn't carry data for it, we consider it to be
184 	 * non-authoritative for meta data as well.  This is consistent
185 	 * with GNU tar and BSD pax.  */
186 	assertIsReg("link1a", 0755 & ~UMASK);
187 	assertFileSize("link1a", sizeof(data));
188 	assertFileNLinks("link1a", 2);
189 	assertIsHardlink("link1a", "link1b");
190 
191 	/* Test #2: Should produce identical results to test #1 */
192 	/* Note that marking a hardlink with size = 0 is treated the
193 	 * same as having an unset size.  This is partly for backwards
194 	 * compatibility (we used to not have unset tracking, so
195 	 * relied on size == 0) and partly to match the model used by
196 	 * common file formats that store a size of zero for
197 	 * hardlinks. */
198 	assertIsReg("link2a", 0755 & ~UMASK);
199 	assertFileSize("link2a", sizeof(data));
200 	assertFileNLinks("link2a", 2);
201 	assertIsHardlink("link2a", "link2b");
202 
203 	/* Test #3 */
204 	assertIsReg("link3a", 0755 & ~UMASK);
205 	assertFileSize("link3a", sizeof(data));
206 	assertFileNLinks("link3a", 2);
207 	assertIsHardlink("link3a", "link3b");
208 
209 	/* Test #4 */
210 	assertIsReg("link4a", 0755 & ~UMASK);
211 	assertFileNLinks("link4a", 2);
212 	assertFileSize("link4a", sizeof(data));
213 	assertIsHardlink("link4a", "link4b");
214 #endif
215 }
216