1 /* 2 * Copyright (c) 2011-2012 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@dragonflybsd.org> 6 * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 3. Neither the name of The DragonFly Project nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific, prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/types.h> 37 #include <sys/stat.h> 38 #include <sys/time.h> 39 #include <sys/ioctl.h> 40 #include <sys/mount.h> 41 #include <sys/statvfs.h> 42 #include <sys/diskslice.h> 43 #include <unistd.h> 44 #include <fcntl.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <stdint.h> 48 #include <string.h> 49 #include <errno.h> 50 #include <err.h> 51 #include <uuid.h> 52 53 #include <vfs/hammer2/hammer2_disk.h> 54 #include <vfs/hammer2/hammer2_ioctl.h> 55 56 #include "hammer2_subs.h" 57 58 /* 59 * Obtain a file descriptor that the caller can execute ioctl()'s on. 60 */ 61 int 62 hammer2_ioctl_handle(const char *sel_path) 63 { 64 struct hammer2_ioc_version info; 65 int fd; 66 67 if (sel_path == NULL) 68 sel_path = "."; 69 70 fd = open(sel_path, O_RDONLY, 0); 71 if (fd < 0) { 72 fprintf(stderr, "hammer2: Unable to open %s: %s\n", 73 sel_path, strerror(errno)); 74 return(-1); 75 } 76 if (ioctl(fd, HAMMER2IOC_VERSION_GET, &info) < 0) { 77 fprintf(stderr, "hammer2: '%s' is not a hammer2 filesystem\n", 78 sel_path); 79 close(fd); 80 return(-1); 81 } 82 return (fd); 83 } 84 85 const char * 86 hammer2_time64_to_str(uint64_t htime64, char **strp) 87 { 88 struct tm *tp; 89 time_t t; 90 91 if (*strp) { 92 free(*strp); 93 *strp = NULL; 94 } 95 *strp = malloc(64); 96 t = htime64 / 1000000; 97 tp = localtime(&t); 98 strftime(*strp, 64, "%d-%b-%Y %H:%M:%S", tp); 99 return (*strp); 100 } 101 102 const char * 103 hammer2_uuid_to_str(const uuid_t *uuid, char **strp) 104 { 105 uint32_t status; 106 if (*strp) { 107 free(*strp); 108 *strp = NULL; 109 } 110 uuid_to_string(uuid, strp, &status); 111 return (*strp); 112 } 113 114 const char * 115 hammer2_iptype_to_str(uint8_t type) 116 { 117 switch(type) { 118 case HAMMER2_OBJTYPE_UNKNOWN: 119 return("UNKNOWN"); 120 case HAMMER2_OBJTYPE_DIRECTORY: 121 return("DIR"); 122 case HAMMER2_OBJTYPE_REGFILE: 123 return("FILE"); 124 case HAMMER2_OBJTYPE_FIFO: 125 return("FIFO"); 126 case HAMMER2_OBJTYPE_CDEV: 127 return("CDEV"); 128 case HAMMER2_OBJTYPE_BDEV: 129 return("BDEV"); 130 case HAMMER2_OBJTYPE_SOFTLINK: 131 return("SOFTLINK"); 132 case HAMMER2_OBJTYPE_SOCKET: 133 return("SOCKET"); 134 case HAMMER2_OBJTYPE_WHITEOUT: 135 return("WHITEOUT"); 136 default: 137 return("ILLEGAL"); 138 } 139 } 140 141 const char * 142 hammer2_pfstype_to_str(uint8_t type) 143 { 144 switch(type) { 145 case HAMMER2_PFSTYPE_NONE: 146 return("NONE"); 147 case HAMMER2_PFSTYPE_SUPROOT: 148 return("SUPROOT"); 149 case HAMMER2_PFSTYPE_DUMMY: 150 return("DUMMY"); 151 case HAMMER2_PFSTYPE_CACHE: 152 return("CACHE"); 153 case HAMMER2_PFSTYPE_SLAVE: 154 return("SLAVE"); 155 case HAMMER2_PFSTYPE_SOFT_SLAVE: 156 return("SOFT_SLAVE"); 157 case HAMMER2_PFSTYPE_SOFT_MASTER: 158 return("SOFT_MASTER"); 159 case HAMMER2_PFSTYPE_MASTER: 160 return("MASTER"); 161 default: 162 return("ILLEGAL"); 163 } 164 } 165 166 const char * 167 hammer2_pfssubtype_to_str(uint8_t subtype) 168 { 169 switch(subtype) { 170 case HAMMER2_PFSSUBTYPE_NONE: 171 return("NONE"); 172 case HAMMER2_PFSSUBTYPE_SNAPSHOT: 173 return("SNAPSHOT"); 174 case HAMMER2_PFSSUBTYPE_AUTOSNAP: 175 return("AUTOSNAP"); 176 default: 177 return("ILLEGAL"); 178 } 179 } 180 181 const char * 182 hammer2_breftype_to_str(uint8_t type) 183 { 184 switch(type) { 185 case HAMMER2_BREF_TYPE_EMPTY: 186 return("empty"); 187 case HAMMER2_BREF_TYPE_INODE: 188 return("inode"); 189 case HAMMER2_BREF_TYPE_INDIRECT: 190 return("indirect"); 191 case HAMMER2_BREF_TYPE_DATA: 192 return("data"); 193 case HAMMER2_BREF_TYPE_DIRENT: 194 return("dirent"); 195 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 196 return("freemap_node"); 197 case HAMMER2_BREF_TYPE_FREEMAP_LEAF: 198 return("freemap_leaf"); 199 case HAMMER2_BREF_TYPE_INVALID: 200 return("invalid"); 201 case HAMMER2_BREF_TYPE_FREEMAP: 202 return("freemap"); 203 case HAMMER2_BREF_TYPE_VOLUME: 204 return("volume"); 205 default: 206 return("unknown"); 207 } 208 } 209 210 const char * 211 hammer2_compmode_to_str(uint8_t comp_algo) 212 { 213 static char buf[64]; 214 static const char *comps[] = HAMMER2_COMP_STRINGS; 215 int comp = HAMMER2_DEC_ALGO(comp_algo); 216 int level = HAMMER2_DEC_LEVEL(comp_algo); 217 218 if (level) { 219 if (comp >= 0 && comp < HAMMER2_COMP_STRINGS_COUNT) 220 snprintf(buf, sizeof(buf), "%s:%d", 221 comps[comp], level); 222 else 223 snprintf(buf, sizeof(buf), "unknown(%d):%d", 224 comp, level); 225 } else { 226 if (comp >= 0 && comp < HAMMER2_COMP_STRINGS_COUNT) 227 snprintf(buf, sizeof(buf), "%s:default", 228 comps[comp]); 229 else 230 snprintf(buf, sizeof(buf), "unknown(%d):default", 231 comp); 232 } 233 return (buf); 234 } 235 236 const char * 237 hammer2_checkmode_to_str(uint8_t check_algo) 238 { 239 static char buf[64]; 240 static const char *checks[] = HAMMER2_CHECK_STRINGS; 241 int check = HAMMER2_DEC_ALGO(check_algo); 242 int level = HAMMER2_DEC_LEVEL(check_algo); 243 244 /* 245 * NOTE: Check algorithms normally do not encode any level. 246 */ 247 if (level) { 248 if (check >= 0 && check < HAMMER2_CHECK_STRINGS_COUNT) 249 snprintf(buf, sizeof(buf), "%s:%d", 250 checks[check], level); 251 else 252 snprintf(buf, sizeof(buf), "unknown(%d):%d", 253 check, level); 254 } else { 255 if (check >= 0 && check < HAMMER2_CHECK_STRINGS_COUNT) 256 snprintf(buf, sizeof(buf), "%s", checks[check]); 257 else 258 snprintf(buf, sizeof(buf), "unknown(%d)", check); 259 } 260 return (buf); 261 } 262 263 const char * 264 sizetostr(hammer2_off_t size) 265 { 266 static char buf[32]; 267 268 if (size < 1024 / 2) { 269 snprintf(buf, sizeof(buf), "%6.2fB", (double)size); 270 } else if (size < 1024 * 1024 / 2) { 271 snprintf(buf, sizeof(buf), "%6.2fKB", 272 (double)size / 1024); 273 } else if (size < 1024 * 1024 * 1024LL / 2) { 274 snprintf(buf, sizeof(buf), "%6.2fMB", 275 (double)size / (1024 * 1024)); 276 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 277 snprintf(buf, sizeof(buf), "%6.2fGB", 278 (double)size / (1024 * 1024 * 1024LL)); 279 } else { 280 snprintf(buf, sizeof(buf), "%6.2fTB", 281 (double)size / (1024 * 1024 * 1024LL * 1024LL)); 282 } 283 return(buf); 284 } 285 286 const char * 287 counttostr(hammer2_off_t size) 288 { 289 static char buf[32]; 290 291 if (size < 1024 / 2) { 292 snprintf(buf, sizeof(buf), "%jd", 293 (intmax_t)size); 294 } else if (size < 1024 * 1024 / 2) { 295 snprintf(buf, sizeof(buf), "%jd", 296 (intmax_t)size); 297 } else if (size < 1024 * 1024 * 1024LL / 2) { 298 snprintf(buf, sizeof(buf), "%6.2fM", 299 (double)size / (1024 * 1024)); 300 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 301 snprintf(buf, sizeof(buf), "%6.2fG", 302 (double)(size / (1024 * 1024 * 1024LL))); 303 } else { 304 snprintf(buf, sizeof(buf), "%6.2fT", 305 (double)(size / (1024 * 1024 * 1024LL * 1024LL))); 306 } 307 return(buf); 308 } 309 310 hammer2_off_t 311 check_volume(int fd) 312 { 313 struct partinfo pinfo; 314 struct stat st; 315 hammer2_off_t size; 316 317 /* 318 * Get basic information about the volume 319 */ 320 if (ioctl(fd, DIOCGPART, &pinfo) < 0) { 321 /* 322 * Allow the formatting of regular files as HAMMER2 volumes 323 */ 324 if (fstat(fd, &st) < 0) 325 err(1, "Unable to stat fd %d", fd); 326 if (!S_ISREG(st.st_mode)) 327 errx(1, "Unsupported file type for fd %d", fd); 328 size = st.st_size; 329 } else { 330 /* 331 * When formatting a block device as a HAMMER2 volume the 332 * sector size must be compatible. HAMMER2 uses 64K 333 * filesystem buffers but logical buffers for direct I/O 334 * can be as small as HAMMER2_LOGSIZE (16KB). 335 */ 336 if (pinfo.reserved_blocks) { 337 errx(1, "HAMMER2 cannot be placed in a partition " 338 "which overlaps the disklabel or MBR"); 339 } 340 if (pinfo.media_blksize > HAMMER2_PBUFSIZE || 341 HAMMER2_PBUFSIZE % pinfo.media_blksize) { 342 errx(1, "A media sector size of %d is not supported", 343 pinfo.media_blksize); 344 } 345 size = pinfo.media_size; 346 } 347 return(size); 348 } 349 350 /* 351 * Borrow HAMMER1's directory hash algorithm #1 with a few modifications. 352 * The filename is split into fields which are hashed separately and then 353 * added together. 354 * 355 * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets 356 * it to 0), this is because bit63=0 is used for hidden hardlinked inodes. 357 * (This means we do not need to do a 0-check/or-with-0x100000000 either). 358 * 359 * Also, the iscsi crc code is used instead of the old crc32 code. 360 */ 361 hammer2_key_t 362 dirhash(const char *aname, size_t len) 363 { 364 uint32_t crcx; 365 uint64_t key; 366 size_t i; 367 size_t j; 368 369 key = 0; 370 371 /* 372 * m32 373 */ 374 crcx = 0; 375 for (i = j = 0; i < len; ++i) { 376 if (aname[i] == '.' || 377 aname[i] == '-' || 378 aname[i] == '_' || 379 aname[i] == '~') { 380 if (i != j) 381 crcx += hammer2_icrc32(aname + j, i - j); 382 j = i + 1; 383 } 384 } 385 if (i != j) 386 crcx += hammer2_icrc32(aname + j, i - j); 387 388 /* 389 * The directory hash utilizes the top 32 bits of the 64-bit key. 390 * Bit 63 must be set to 1. 391 */ 392 crcx |= 0x80000000U; 393 key |= (uint64_t)crcx << 32; 394 395 /* 396 * l16 - crc of entire filename 397 * 398 * This crc reduces degenerate hash collision conditions. 399 */ 400 crcx = hammer2_icrc32(aname, len); 401 crcx = crcx ^ (crcx << 16); 402 key |= crcx & 0xFFFF0000U; 403 404 /* 405 * Set bit 15. This allows readdir to strip bit 63 so a positive 406 * 64-bit cookie/offset can always be returned, and still guarantee 407 * that the values 0x0000-0x7FFF are available for artificial entries. 408 * ('.' and '..'). 409 */ 410 key |= 0x8000U; 411 412 return (key); 413 } 414 415 char ** 416 get_hammer2_mounts(int *acp) 417 { 418 struct statfs *fs; 419 char **av; 420 int n; 421 int w; 422 int i; 423 424 /* 425 * Get a stable list of mount points 426 */ 427 again: 428 n = getfsstat(NULL, 0, MNT_NOWAIT); 429 av = calloc(n, sizeof(char *)); 430 fs = calloc(n, sizeof(struct statfs)); 431 if (getfsstat(fs, sizeof(*fs) * n, MNT_NOWAIT) != n) { 432 free(av); 433 free(fs); 434 goto again; 435 } 436 437 /* 438 * Pull out hammer2 filesystems only 439 */ 440 for (i = w = 0; i < n; ++i) { 441 if (strcmp(fs[i].f_fstypename, "hammer2") != 0) 442 continue; 443 av[w++] = strdup(fs[i].f_mntonname); 444 } 445 *acp = w; 446 free(fs); 447 448 return av; 449 } 450 451 void 452 put_hammer2_mounts(int ac, char **av) 453 { 454 while (--ac >= 0) 455 free(av[ac]); 456 free(av); 457 } 458