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