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/time.h> 38 #include <sys/ioctl.h> 39 #include <sys/mount.h> 40 #include <sys/statvfs.h> 41 #include <sys/diskslice.h> 42 #include <unistd.h> 43 #include <fcntl.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <errno.h> 48 #include <err.h> 49 #include <uuid.h> 50 51 #include <vfs/hammer2/hammer2_disk.h> 52 #include <vfs/hammer2/hammer2_ioctl.h> 53 54 #include "hammer2_subs.h" 55 56 /* 57 * Obtain a file descriptor that the caller can execute ioctl()'s on. 58 */ 59 int 60 hammer2_ioctl_handle(const char *sel_path) 61 { 62 struct hammer2_ioc_version info; 63 int fd; 64 65 if (sel_path == NULL) 66 sel_path = "."; 67 68 fd = open(sel_path, O_RDONLY, 0); 69 if (fd < 0) { 70 fprintf(stderr, "hammer2: Unable to open %s: %s\n", 71 sel_path, strerror(errno)); 72 return(-1); 73 } 74 if (ioctl(fd, HAMMER2IOC_VERSION_GET, &info) < 0) { 75 fprintf(stderr, "hammer2: '%s' is not a hammer2 filesystem\n", 76 sel_path); 77 close(fd); 78 return(-1); 79 } 80 return (fd); 81 } 82 83 const char * 84 hammer2_time64_to_str(uint64_t htime64, char **strp) 85 { 86 struct tm *tp; 87 time_t t; 88 89 if (*strp) { 90 free(*strp); 91 *strp = NULL; 92 } 93 *strp = malloc(64); 94 t = htime64 / 1000000; 95 tp = localtime(&t); 96 strftime(*strp, 64, "%d-%b-%Y %H:%M:%S", tp); 97 return (*strp); 98 } 99 100 const char * 101 hammer2_uuid_to_str(const uuid_t *uuid, char **strp) 102 { 103 uint32_t status; 104 if (*strp) { 105 free(*strp); 106 *strp = NULL; 107 } 108 uuid_to_string(uuid, strp, &status); 109 return (*strp); 110 } 111 112 const char * 113 hammer2_iptype_to_str(uint8_t type) 114 { 115 switch(type) { 116 case HAMMER2_OBJTYPE_UNKNOWN: 117 return("UNKNOWN"); 118 case HAMMER2_OBJTYPE_DIRECTORY: 119 return("DIR"); 120 case HAMMER2_OBJTYPE_REGFILE: 121 return("FILE"); 122 case HAMMER2_OBJTYPE_FIFO: 123 return("FIFO"); 124 case HAMMER2_OBJTYPE_CDEV: 125 return("CDEV"); 126 case HAMMER2_OBJTYPE_BDEV: 127 return("BDEV"); 128 case HAMMER2_OBJTYPE_SOFTLINK: 129 return("SOFTLINK"); 130 case HAMMER2_OBJTYPE_SOCKET: 131 return("SOCKET"); 132 case HAMMER2_OBJTYPE_WHITEOUT: 133 return("WHITEOUT"); 134 default: 135 return("ILLEGAL"); 136 } 137 } 138 139 const char * 140 hammer2_pfstype_to_str(uint8_t type) 141 { 142 switch(type) { 143 case HAMMER2_PFSTYPE_NONE: 144 return("NONE"); 145 case HAMMER2_PFSTYPE_SUPROOT: 146 return("SUPROOT"); 147 case HAMMER2_PFSTYPE_DUMMY: 148 return("DUMMY"); 149 case HAMMER2_PFSTYPE_CACHE: 150 return("CACHE"); 151 case HAMMER2_PFSTYPE_SLAVE: 152 return("SLAVE"); 153 case HAMMER2_PFSTYPE_SOFT_SLAVE: 154 return("SOFT_SLAVE"); 155 case HAMMER2_PFSTYPE_SOFT_MASTER: 156 return("SOFT_MASTER"); 157 case HAMMER2_PFSTYPE_MASTER: 158 return("MASTER"); 159 default: 160 return("ILLEGAL"); 161 } 162 } 163 164 const char * 165 hammer2_pfssubtype_to_str(uint8_t subtype) 166 { 167 switch(subtype) { 168 case HAMMER2_PFSSUBTYPE_NONE: 169 return("NONE"); 170 case HAMMER2_PFSSUBTYPE_SNAPSHOT: 171 return("SNAPSHOT"); 172 case HAMMER2_PFSSUBTYPE_AUTOSNAP: 173 return("AUTOSNAP"); 174 default: 175 return("ILLEGAL"); 176 } 177 } 178 179 const char * 180 hammer2_breftype_to_str(uint8_t type) 181 { 182 switch(type) { 183 case HAMMER2_BREF_TYPE_EMPTY: 184 return("empty"); 185 case HAMMER2_BREF_TYPE_INODE: 186 return("inode"); 187 case HAMMER2_BREF_TYPE_INDIRECT: 188 return("indirect"); 189 case HAMMER2_BREF_TYPE_DATA: 190 return("data"); 191 case HAMMER2_BREF_TYPE_DIRENT: 192 return("dirent"); 193 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 194 return("freemap_node"); 195 case HAMMER2_BREF_TYPE_FREEMAP_LEAF: 196 return("freemap_leaf"); 197 case HAMMER2_BREF_TYPE_INVALID: 198 return("invalid"); 199 case HAMMER2_BREF_TYPE_FREEMAP: 200 return("freemap"); 201 case HAMMER2_BREF_TYPE_VOLUME: 202 return("volume"); 203 default: 204 return("unknown"); 205 } 206 } 207 208 const char * 209 sizetostr(hammer2_off_t size) 210 { 211 static char buf[32]; 212 213 if (size < 1024 / 2) { 214 snprintf(buf, sizeof(buf), "%6.2fB", (double)size); 215 } else if (size < 1024 * 1024 / 2) { 216 snprintf(buf, sizeof(buf), "%6.2fKB", 217 (double)size / 1024); 218 } else if (size < 1024 * 1024 * 1024LL / 2) { 219 snprintf(buf, sizeof(buf), "%6.2fMB", 220 (double)size / (1024 * 1024)); 221 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 222 snprintf(buf, sizeof(buf), "%6.2fGB", 223 (double)size / (1024 * 1024 * 1024LL)); 224 } else { 225 snprintf(buf, sizeof(buf), "%6.2fTB", 226 (double)size / (1024 * 1024 * 1024LL * 1024LL)); 227 } 228 return(buf); 229 } 230 231 const char * 232 counttostr(hammer2_off_t size) 233 { 234 static char buf[32]; 235 236 if (size < 1024 / 2) { 237 snprintf(buf, sizeof(buf), "%jd", 238 (intmax_t)size); 239 } else if (size < 1024 * 1024 / 2) { 240 snprintf(buf, sizeof(buf), "%jd", 241 (intmax_t)size); 242 } else if (size < 1024 * 1024 * 1024LL / 2) { 243 snprintf(buf, sizeof(buf), "%6.2fM", 244 (double)size / (1024 * 1024)); 245 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 246 snprintf(buf, sizeof(buf), "%6.2fG", 247 (double)(size / (1024 * 1024 * 1024LL))); 248 } else { 249 snprintf(buf, sizeof(buf), "%6.2fT", 250 (double)(size / (1024 * 1024 * 1024LL * 1024LL))); 251 } 252 return(buf); 253 } 254 255 hammer2_off_t 256 check_volume(int fd) 257 { 258 struct partinfo pinfo; 259 struct stat st; 260 hammer2_off_t size; 261 262 /* 263 * Get basic information about the volume 264 */ 265 if (ioctl(fd, DIOCGPART, &pinfo) < 0) { 266 /* 267 * Allow the formatting of regular files as HAMMER2 volumes 268 */ 269 if (fstat(fd, &st) < 0) 270 err(1, "Unable to stat fd %d", fd); 271 if (!S_ISREG(st.st_mode)) 272 errx(1, "Unsupported file type for fd %d", fd); 273 size = st.st_size; 274 } else { 275 /* 276 * When formatting a block device as a HAMMER2 volume the 277 * sector size must be compatible. HAMMER2 uses 64K 278 * filesystem buffers but logical buffers for direct I/O 279 * can be as small as HAMMER2_LOGSIZE (16KB). 280 */ 281 if (pinfo.reserved_blocks) { 282 errx(1, "HAMMER2 cannot be placed in a partition " 283 "which overlaps the disklabel or MBR"); 284 } 285 if (pinfo.media_blksize > HAMMER2_PBUFSIZE || 286 HAMMER2_PBUFSIZE % pinfo.media_blksize) { 287 errx(1, "A media sector size of %d is not supported", 288 pinfo.media_blksize); 289 } 290 size = pinfo.media_size; 291 } 292 return(size); 293 } 294 295 /* 296 * Borrow HAMMER1's directory hash algorithm #1 with a few modifications. 297 * The filename is split into fields which are hashed separately and then 298 * added together. 299 * 300 * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets 301 * it to 0), this is because bit63=0 is used for hidden hardlinked inodes. 302 * (This means we do not need to do a 0-check/or-with-0x100000000 either). 303 * 304 * Also, the iscsi crc code is used instead of the old crc32 code. 305 */ 306 hammer2_key_t 307 dirhash(const unsigned char *name, size_t len) 308 { 309 const unsigned char *aname = name; 310 uint32_t crcx; 311 uint64_t key; 312 size_t i; 313 size_t j; 314 315 /* 316 * Filesystem version 6 or better will create directories 317 * using the ALG1 dirhash. This hash breaks the filename 318 * up into domains separated by special characters and 319 * hashes each domain independently. 320 * 321 * We also do a simple sub-sort using the first character 322 * of the filename in the top 5-bits. 323 */ 324 key = 0; 325 326 /* 327 * m32 328 */ 329 crcx = 0; 330 for (i = j = 0; i < len; ++i) { 331 if (aname[i] == '.' || 332 aname[i] == '-' || 333 aname[i] == '_' || 334 aname[i] == '~') { 335 if (i != j) 336 crcx += hammer2_icrc32(aname + j, i - j); 337 j = i + 1; 338 } 339 } 340 if (i != j) 341 crcx += hammer2_icrc32(aname + j, i - j); 342 343 /* 344 * The directory hash utilizes the top 32 bits of the 64-bit key. 345 * Bit 63 must be set to 1. 346 */ 347 crcx |= 0x80000000U; 348 key |= (uint64_t)crcx << 32; 349 350 /* 351 * l16 - crc of entire filename 352 * 353 * This crc reduces degenerate hash collision conditions 354 */ 355 crcx = hammer2_icrc32(aname, len); 356 crcx = crcx ^ (crcx << 16); 357 key |= crcx & 0xFFFF0000U; 358 359 /* 360 * Set bit 15. This allows readdir to strip bit 63 so a positive 361 * 64-bit cookie/offset can always be returned, and still guarantee 362 * that the values 0x0000-0x7FFF are available for artificial entries. 363 * ('.' and '..'). 364 */ 365 key |= 0x8000U; 366 367 return (key); 368 } 369 370 char ** 371 get_hammer2_mounts(int *acp) 372 { 373 struct statfs *fs; 374 char **av; 375 int n; 376 int w; 377 int i; 378 379 /* 380 * Get a stable list of mount points 381 */ 382 again: 383 n = getfsstat(NULL, 0, MNT_NOWAIT); 384 av = calloc(n, sizeof(char *)); 385 fs = calloc(n, sizeof(struct statfs)); 386 if (getfsstat(fs, sizeof(*fs) * n, MNT_NOWAIT) != n) { 387 free(av); 388 free(fs); 389 goto again; 390 } 391 392 /* 393 * Pull out hammer2 filesystems only 394 */ 395 for (i = w = 0; i < n; ++i) { 396 if (strcmp(fs[i].f_fstypename, "hammer2") != 0) 397 continue; 398 av[w++] = strdup(fs[i].f_mntonname); 399 } 400 *acp = w; 401 free(fs); 402 403 return av; 404 } 405 406 void 407 put_hammer2_mounts(int ac, char **av) 408 { 409 while (--ac >= 0) 410 free(av[ac]); 411 free(av); 412 } 413