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