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 sizetostr(hammer2_off_t size) 212 { 213 static char buf[32]; 214 215 if (size < 1024 / 2) { 216 snprintf(buf, sizeof(buf), "%6.2fB", (double)size); 217 } else if (size < 1024 * 1024 / 2) { 218 snprintf(buf, sizeof(buf), "%6.2fKB", 219 (double)size / 1024); 220 } else if (size < 1024 * 1024 * 1024LL / 2) { 221 snprintf(buf, sizeof(buf), "%6.2fMB", 222 (double)size / (1024 * 1024)); 223 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 224 snprintf(buf, sizeof(buf), "%6.2fGB", 225 (double)size / (1024 * 1024 * 1024LL)); 226 } else { 227 snprintf(buf, sizeof(buf), "%6.2fTB", 228 (double)size / (1024 * 1024 * 1024LL * 1024LL)); 229 } 230 return(buf); 231 } 232 233 const char * 234 counttostr(hammer2_off_t size) 235 { 236 static char buf[32]; 237 238 if (size < 1024 / 2) { 239 snprintf(buf, sizeof(buf), "%jd", 240 (intmax_t)size); 241 } else if (size < 1024 * 1024 / 2) { 242 snprintf(buf, sizeof(buf), "%jd", 243 (intmax_t)size); 244 } else if (size < 1024 * 1024 * 1024LL / 2) { 245 snprintf(buf, sizeof(buf), "%6.2fM", 246 (double)size / (1024 * 1024)); 247 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 248 snprintf(buf, sizeof(buf), "%6.2fG", 249 (double)(size / (1024 * 1024 * 1024LL))); 250 } else { 251 snprintf(buf, sizeof(buf), "%6.2fT", 252 (double)(size / (1024 * 1024 * 1024LL * 1024LL))); 253 } 254 return(buf); 255 } 256 257 hammer2_off_t 258 check_volume(int fd) 259 { 260 struct partinfo pinfo; 261 struct stat st; 262 hammer2_off_t size; 263 264 /* 265 * Get basic information about the volume 266 */ 267 if (ioctl(fd, DIOCGPART, &pinfo) < 0) { 268 /* 269 * Allow the formatting of regular files as HAMMER2 volumes 270 */ 271 if (fstat(fd, &st) < 0) 272 err(1, "Unable to stat fd %d", fd); 273 if (!S_ISREG(st.st_mode)) 274 errx(1, "Unsupported file type for fd %d", fd); 275 size = st.st_size; 276 } else { 277 /* 278 * When formatting a block device as a HAMMER2 volume the 279 * sector size must be compatible. HAMMER2 uses 64K 280 * filesystem buffers but logical buffers for direct I/O 281 * can be as small as HAMMER2_LOGSIZE (16KB). 282 */ 283 if (pinfo.reserved_blocks) { 284 errx(1, "HAMMER2 cannot be placed in a partition " 285 "which overlaps the disklabel or MBR"); 286 } 287 if (pinfo.media_blksize > HAMMER2_PBUFSIZE || 288 HAMMER2_PBUFSIZE % pinfo.media_blksize) { 289 errx(1, "A media sector size of %d is not supported", 290 pinfo.media_blksize); 291 } 292 size = pinfo.media_size; 293 } 294 return(size); 295 } 296 297 /* 298 * Borrow HAMMER1's directory hash algorithm #1 with a few modifications. 299 * The filename is split into fields which are hashed separately and then 300 * added together. 301 * 302 * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets 303 * it to 0), this is because bit63=0 is used for hidden hardlinked inodes. 304 * (This means we do not need to do a 0-check/or-with-0x100000000 either). 305 * 306 * Also, the iscsi crc code is used instead of the old crc32 code. 307 */ 308 hammer2_key_t 309 dirhash(const char *aname, size_t len) 310 { 311 uint32_t crcx; 312 uint64_t key; 313 size_t i; 314 size_t j; 315 316 key = 0; 317 318 /* 319 * m32 320 */ 321 crcx = 0; 322 for (i = j = 0; i < len; ++i) { 323 if (aname[i] == '.' || 324 aname[i] == '-' || 325 aname[i] == '_' || 326 aname[i] == '~') { 327 if (i != j) 328 crcx += hammer2_icrc32(aname + j, i - j); 329 j = i + 1; 330 } 331 } 332 if (i != j) 333 crcx += hammer2_icrc32(aname + j, i - j); 334 335 /* 336 * The directory hash utilizes the top 32 bits of the 64-bit key. 337 * Bit 63 must be set to 1. 338 */ 339 crcx |= 0x80000000U; 340 key |= (uint64_t)crcx << 32; 341 342 /* 343 * l16 - crc of entire filename 344 * 345 * This crc reduces degenerate hash collision conditions. 346 */ 347 crcx = hammer2_icrc32(aname, len); 348 crcx = crcx ^ (crcx << 16); 349 key |= crcx & 0xFFFF0000U; 350 351 /* 352 * Set bit 15. This allows readdir to strip bit 63 so a positive 353 * 64-bit cookie/offset can always be returned, and still guarantee 354 * that the values 0x0000-0x7FFF are available for artificial entries. 355 * ('.' and '..'). 356 */ 357 key |= 0x8000U; 358 359 return (key); 360 } 361 362 char ** 363 get_hammer2_mounts(int *acp) 364 { 365 struct statfs *fs; 366 char **av; 367 int n; 368 int w; 369 int i; 370 371 /* 372 * Get a stable list of mount points 373 */ 374 again: 375 n = getfsstat(NULL, 0, MNT_NOWAIT); 376 av = calloc(n, sizeof(char *)); 377 fs = calloc(n, sizeof(struct statfs)); 378 if (getfsstat(fs, sizeof(*fs) * n, MNT_NOWAIT) != n) { 379 free(av); 380 free(fs); 381 goto again; 382 } 383 384 /* 385 * Pull out hammer2 filesystems only 386 */ 387 for (i = w = 0; i < n; ++i) { 388 if (strcmp(fs[i].f_fstypename, "hammer2") != 0) 389 continue; 390 av[w++] = strdup(fs[i].f_mntonname); 391 } 392 *acp = w; 393 free(fs); 394 395 return av; 396 } 397 398 void 399 put_hammer2_mounts(int ac, char **av) 400 { 401 while (--ac >= 0) 402 free(av[ac]); 403 free(av); 404 } 405