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