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(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_breftype_to_str(uint8_t type) 164 { 165 switch (type) { 166 case HAMMER2_BREF_TYPE_EMPTY: 167 return("empty"); 168 case HAMMER2_BREF_TYPE_INODE: 169 return("inode"); 170 case HAMMER2_BREF_TYPE_INDIRECT: 171 return("indirect"); 172 case HAMMER2_BREF_TYPE_DATA: 173 return("data"); 174 case HAMMER2_BREF_TYPE_DIRENT: 175 return("dirent"); 176 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 177 return("freemap_node"); 178 case HAMMER2_BREF_TYPE_FREEMAP_LEAF: 179 return("freemap_leaf"); 180 case HAMMER2_BREF_TYPE_FREEMAP: 181 return("freemap"); 182 case HAMMER2_BREF_TYPE_VOLUME: 183 return("volume"); 184 default: 185 return("unknown"); 186 } 187 } 188 189 const char * 190 sizetostr(hammer2_off_t size) 191 { 192 static char buf[32]; 193 194 if (size < 1024 / 2) { 195 snprintf(buf, sizeof(buf), "%6.2fB", (double)size); 196 } else if (size < 1024 * 1024 / 2) { 197 snprintf(buf, sizeof(buf), "%6.2fKB", 198 (double)size / 1024); 199 } else if (size < 1024 * 1024 * 1024LL / 2) { 200 snprintf(buf, sizeof(buf), "%6.2fMB", 201 (double)size / (1024 * 1024)); 202 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 203 snprintf(buf, sizeof(buf), "%6.2fGB", 204 (double)size / (1024 * 1024 * 1024LL)); 205 } else { 206 snprintf(buf, sizeof(buf), "%6.2fTB", 207 (double)size / (1024 * 1024 * 1024LL * 1024LL)); 208 } 209 return(buf); 210 } 211 212 const char * 213 counttostr(hammer2_off_t size) 214 { 215 static char buf[32]; 216 217 if (size < 1024 / 2) { 218 snprintf(buf, sizeof(buf), "%jd", 219 (intmax_t)size); 220 } else if (size < 1024 * 1024 / 2) { 221 snprintf(buf, sizeof(buf), "%jd", 222 (intmax_t)size); 223 } else if (size < 1024 * 1024 * 1024LL / 2) { 224 snprintf(buf, sizeof(buf), "%6.2fM", 225 (double)size / (1024 * 1024)); 226 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) { 227 snprintf(buf, sizeof(buf), "%6.2fG", 228 (double)(size / (1024 * 1024 * 1024LL))); 229 } else { 230 snprintf(buf, sizeof(buf), "%6.2fT", 231 (double)(size / (1024 * 1024 * 1024LL * 1024LL))); 232 } 233 return(buf); 234 } 235 236 /* 237 * Borrow HAMMER1's directory hash algorithm #1 with a few modifications. 238 * The filename is split into fields which are hashed separately and then 239 * added together. 240 * 241 * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets 242 * it to 0), this is because bit63=0 is used for hidden hardlinked inodes. 243 * (This means we do not need to do a 0-check/or-with-0x100000000 either). 244 * 245 * Also, the iscsi crc code is used instead of the old crc32 code. 246 */ 247 hammer2_key_t 248 dirhash(const unsigned char *name, size_t len) 249 { 250 const unsigned char *aname = name; 251 uint32_t crcx; 252 uint64_t key; 253 size_t i; 254 size_t j; 255 256 /* 257 * Filesystem version 6 or better will create directories 258 * using the ALG1 dirhash. This hash breaks the filename 259 * up into domains separated by special characters and 260 * hashes each domain independently. 261 * 262 * We also do a simple sub-sort using the first character 263 * of the filename in the top 5-bits. 264 */ 265 key = 0; 266 267 /* 268 * m32 269 */ 270 crcx = 0; 271 for (i = j = 0; i < len; ++i) { 272 if (aname[i] == '.' || 273 aname[i] == '-' || 274 aname[i] == '_' || 275 aname[i] == '~') { 276 if (i != j) 277 crcx += hammer2_icrc32(aname + j, i - j); 278 j = i + 1; 279 } 280 } 281 if (i != j) 282 crcx += hammer2_icrc32(aname + j, i - j); 283 284 /* 285 * The directory hash utilizes the top 32 bits of the 64-bit key. 286 * Bit 63 must be set to 1. 287 */ 288 crcx |= 0x80000000U; 289 key |= (uint64_t)crcx << 32; 290 291 /* 292 * l16 - crc of entire filename 293 * 294 * This crc reduces degenerate hash collision conditions 295 */ 296 crcx = hammer2_icrc32(aname, len); 297 crcx = crcx ^ (crcx << 16); 298 key |= crcx & 0xFFFF0000U; 299 300 /* 301 * Set bit 15. This allows readdir to strip bit 63 so a positive 302 * 64-bit cookie/offset can always be returned, and still guarantee 303 * that the values 0x0000-0x7FFF are available for artificial entries. 304 * ('.' and '..'). 305 */ 306 key |= 0x8000U; 307 308 return (key); 309 } 310 311 char ** 312 get_hammer2_mounts(int *acp) 313 { 314 struct statfs *fs; 315 char **av; 316 int n; 317 int w; 318 int i; 319 320 /* 321 * Get a stable list of mount points 322 */ 323 again: 324 n = getfsstat(NULL, 0, MNT_NOWAIT); 325 av = calloc(n, sizeof(char *)); 326 fs = calloc(n, sizeof(struct statfs)); 327 if (getfsstat(fs, sizeof(*fs) * n, MNT_NOWAIT) != n) { 328 free(av); 329 free(fs); 330 goto again; 331 } 332 333 /* 334 * Pull out hammer2 filesystems only 335 */ 336 for (i = w = 0; i < n; ++i) { 337 if (strcmp(fs[i].f_fstypename, "hammer2") != 0) 338 continue; 339 av[w++] = strdup(fs[i].f_mntonname); 340 } 341 *acp = w; 342 free(fs); 343 344 return av; 345 } 346 347 void 348 put_hammer2_mounts(int ac, char **av) 349 { 350 while (--ac >= 0) 351 free(av[ac]); 352 free(av); 353 } 354