1 /* 2 * Copyright (c) 2011-2013 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 #include <sys/cdefs.h> 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/types.h> 39 #include <sys/lock.h> 40 #include <sys/uuid.h> 41 #include <sys/dirent.h> 42 43 #include "hammer2.h" 44 45 /* 46 * Mount-wide locks 47 */ 48 49 void 50 hammer2_mount_exlock(hammer2_mount_t *hmp) 51 { 52 ccms_thread_lock(&hmp->vchain.core->cst, CCMS_STATE_EXCLUSIVE); 53 } 54 55 void 56 hammer2_mount_shlock(hammer2_mount_t *hmp) 57 { 58 ccms_thread_lock(&hmp->vchain.core->cst, CCMS_STATE_SHARED); 59 } 60 61 void 62 hammer2_mount_unlock(hammer2_mount_t *hmp) 63 { 64 ccms_thread_unlock(&hmp->vchain.core->cst); 65 } 66 67 void 68 hammer2_voldata_lock(hammer2_mount_t *hmp) 69 { 70 lockmgr(&hmp->voldatalk, LK_EXCLUSIVE); 71 } 72 73 void 74 hammer2_voldata_unlock(hammer2_mount_t *hmp, int modify) 75 { 76 if (modify && 77 (hmp->vchain.flags & HAMMER2_CHAIN_MODIFIED) == 0) { 78 atomic_set_int(&hmp->vchain.flags, HAMMER2_CHAIN_MODIFIED); 79 hammer2_chain_ref(&hmp->vchain); 80 } 81 lockmgr(&hmp->voldatalk, LK_RELEASE); 82 } 83 84 /* 85 * Return the directory entry type for an inode. 86 * 87 * ip must be locked sh/ex. 88 */ 89 int 90 hammer2_get_dtype(hammer2_chain_t *chain) 91 { 92 uint8_t type; 93 94 KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE); 95 96 if ((type = chain->data->ipdata.type) == HAMMER2_OBJTYPE_HARDLINK) 97 type = chain->data->ipdata.target_type; 98 99 switch(type) { 100 case HAMMER2_OBJTYPE_UNKNOWN: 101 return (DT_UNKNOWN); 102 case HAMMER2_OBJTYPE_DIRECTORY: 103 return (DT_DIR); 104 case HAMMER2_OBJTYPE_REGFILE: 105 return (DT_REG); 106 case HAMMER2_OBJTYPE_FIFO: 107 return (DT_FIFO); 108 case HAMMER2_OBJTYPE_CDEV: /* not supported */ 109 return (DT_CHR); 110 case HAMMER2_OBJTYPE_BDEV: /* not supported */ 111 return (DT_BLK); 112 case HAMMER2_OBJTYPE_SOFTLINK: 113 return (DT_LNK); 114 case HAMMER2_OBJTYPE_HARDLINK: /* (never directly associated w/vp) */ 115 return (DT_UNKNOWN); 116 case HAMMER2_OBJTYPE_SOCKET: 117 return (DT_SOCK); 118 case HAMMER2_OBJTYPE_WHITEOUT: /* not supported */ 119 return (DT_UNKNOWN); 120 default: 121 return (DT_UNKNOWN); 122 } 123 /* not reached */ 124 } 125 126 /* 127 * Return the directory entry type for an inode 128 */ 129 int 130 hammer2_get_vtype(hammer2_chain_t *chain) 131 { 132 KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE); 133 134 switch(chain->data->ipdata.type) { 135 case HAMMER2_OBJTYPE_UNKNOWN: 136 return (VBAD); 137 case HAMMER2_OBJTYPE_DIRECTORY: 138 return (VDIR); 139 case HAMMER2_OBJTYPE_REGFILE: 140 return (VREG); 141 case HAMMER2_OBJTYPE_FIFO: 142 return (VFIFO); 143 case HAMMER2_OBJTYPE_CDEV: /* not supported */ 144 return (VCHR); 145 case HAMMER2_OBJTYPE_BDEV: /* not supported */ 146 return (VBLK); 147 case HAMMER2_OBJTYPE_SOFTLINK: 148 return (VLNK); 149 case HAMMER2_OBJTYPE_HARDLINK: /* XXX */ 150 return (VBAD); 151 case HAMMER2_OBJTYPE_SOCKET: 152 return (VSOCK); 153 case HAMMER2_OBJTYPE_WHITEOUT: /* not supported */ 154 return (DT_UNKNOWN); 155 default: 156 return (DT_UNKNOWN); 157 } 158 /* not reached */ 159 } 160 161 u_int8_t 162 hammer2_get_obj_type(enum vtype vtype) 163 { 164 switch(vtype) { 165 case VDIR: 166 return(HAMMER2_OBJTYPE_DIRECTORY); 167 case VREG: 168 return(HAMMER2_OBJTYPE_REGFILE); 169 case VFIFO: 170 return(HAMMER2_OBJTYPE_FIFO); 171 case VSOCK: 172 return(HAMMER2_OBJTYPE_SOCKET); 173 case VCHR: 174 return(HAMMER2_OBJTYPE_CDEV); 175 case VBLK: 176 return(HAMMER2_OBJTYPE_BDEV); 177 case VLNK: 178 return(HAMMER2_OBJTYPE_SOFTLINK); 179 default: 180 return(HAMMER2_OBJTYPE_UNKNOWN); 181 } 182 /* not reached */ 183 } 184 185 /* 186 * Convert a hammer2 64-bit time to a timespec. 187 */ 188 void 189 hammer2_time_to_timespec(u_int64_t xtime, struct timespec *ts) 190 { 191 ts->tv_sec = (unsigned long)(xtime / 1000000); 192 ts->tv_nsec = (unsigned int)(xtime % 1000000) * 1000L; 193 } 194 195 u_int64_t 196 hammer2_timespec_to_time(struct timespec *ts) 197 { 198 u_int64_t xtime; 199 200 xtime = (unsigned)(ts->tv_nsec / 1000) + 201 (unsigned long)ts->tv_sec * 1000000ULL; 202 return(xtime); 203 } 204 205 /* 206 * Convert a uuid to a unix uid or gid 207 */ 208 u_int32_t 209 hammer2_to_unix_xid(uuid_t *uuid) 210 { 211 return(*(u_int32_t *)&uuid->node[2]); 212 } 213 214 void 215 hammer2_guid_to_uuid(uuid_t *uuid, u_int32_t guid) 216 { 217 bzero(uuid, sizeof(*uuid)); 218 *(u_int32_t *)&uuid->node[2] = guid; 219 } 220 221 /* 222 * Borrow HAMMER1's directory hash algorithm #1 with a few modifications. 223 * The filename is split into fields which are hashed separately and then 224 * added together. 225 * 226 * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets 227 * it to 0), this is because bit63=0 is used for hidden hardlinked inodes. 228 * (This means we do not need to do a 0-check/or-with-0x100000000 either). 229 * 230 * Also, the iscsi crc code is used instead of the old crc32 code. 231 */ 232 hammer2_key_t 233 hammer2_dirhash(const unsigned char *name, size_t len) 234 { 235 const unsigned char *aname = name; 236 uint32_t crcx; 237 uint64_t key; 238 size_t i; 239 size_t j; 240 241 key = 0; 242 243 /* 244 * m32 245 */ 246 crcx = 0; 247 for (i = j = 0; i < len; ++i) { 248 if (aname[i] == '.' || 249 aname[i] == '-' || 250 aname[i] == '_' || 251 aname[i] == '~') { 252 if (i != j) 253 crcx += hammer2_icrc32(aname + j, i - j); 254 j = i + 1; 255 } 256 } 257 if (i != j) 258 crcx += hammer2_icrc32(aname + j, i - j); 259 260 /* 261 * The directory hash utilizes the top 32 bits of the 64-bit key. 262 * Bit 63 must be set to 1. 263 */ 264 crcx |= 0x80000000U; 265 key |= (uint64_t)crcx << 32; 266 267 /* 268 * l16 - crc of entire filename 269 * 270 * This crc reduces degenerate hash collision conditions 271 */ 272 crcx = hammer2_icrc32(aname, len); 273 crcx = crcx ^ (crcx << 16); 274 key |= crcx & 0xFFFF0000U; 275 276 /* 277 * Set bit 15. This allows readdir to strip bit 63 so a positive 278 * 64-bit cookie/offset can always be returned, and still guarantee 279 * that the values 0x0000-0x7FFF are available for artificial entries. 280 * ('.' and '..'). 281 */ 282 key |= 0x8000U; 283 284 return (key); 285 } 286 287 #if 0 288 /* 289 * Return the power-of-2 radix greater or equal to 290 * the specified number of bytes. 291 * 292 * Always returns at least the minimum media allocation 293 * size radix, HAMMER2_MIN_RADIX (10), which is 1KB. 294 */ 295 int 296 hammer2_allocsize(size_t bytes) 297 { 298 int radix; 299 300 if (bytes < HAMMER2_MIN_ALLOC) 301 bytes = HAMMER2_MIN_ALLOC; 302 if (bytes == HAMMER2_PBUFSIZE) 303 radix = HAMMER2_PBUFRADIX; 304 else if (bytes >= 16384) 305 radix = 14; 306 else if (bytes >= 1024) 307 radix = 10; 308 else 309 radix = HAMMER2_MIN_RADIX; 310 311 while (((size_t)1 << radix) < bytes) 312 ++radix; 313 return (radix); 314 } 315 316 #endif 317 318 /* 319 * Convert bytes to radix with no limitations 320 */ 321 int 322 hammer2_getradix(size_t bytes) 323 { 324 int radix; 325 326 if (bytes == HAMMER2_PBUFSIZE) 327 radix = HAMMER2_PBUFRADIX; 328 else if (bytes >= HAMMER2_LBUFSIZE) 329 radix = HAMMER2_LBUFRADIX; 330 else if (bytes >= HAMMER2_MIN_ALLOC) /* clamp */ 331 radix = HAMMER2_MIN_RADIX; 332 else 333 radix = 0; 334 335 while (((size_t)1 << radix) < bytes) 336 ++radix; 337 return (radix); 338 } 339 340 /* 341 * ip must be locked sh/ex 342 * 343 * Use 16KB logical buffers for file blocks <= 1MB and 64KB logical buffers 344 * otherwise. The write code may utilize smaller device buffers when 345 * compressing or handling the EOF case, but is not able to coalesce smaller 346 * logical buffers into larger device buffers. 347 * 348 * For now this means that even large files will have a bunch of 16KB blocks 349 * at the beginning of the file. On the plus side this tends to cause small 350 * files to cluster together in the freemap. 351 */ 352 int 353 hammer2_calc_logical(hammer2_inode_t *ip, hammer2_off_t uoff, 354 hammer2_key_t *lbasep, hammer2_key_t *leofp) 355 { 356 #if 0 357 if (uoff < (hammer2_off_t)1024 * 1024) { 358 if (lbasep) 359 *lbasep = uoff & ~HAMMER2_LBUFMASK64; 360 if (leofp) { 361 if (ip->size > (hammer2_key_t)1024 * 1024) 362 *leofp = (hammer2_key_t)1024 * 1024; 363 else 364 *leofp = (ip->size + HAMMER2_LBUFMASK64) & 365 ~HAMMER2_LBUFMASK64; 366 } 367 return (HAMMER2_LBUFSIZE); 368 } else { 369 #endif 370 if (lbasep) 371 *lbasep = uoff & ~HAMMER2_PBUFMASK64; 372 if (leofp) { 373 *leofp = (ip->size + HAMMER2_PBUFMASK64) & 374 ~HAMMER2_PBUFMASK64; 375 } 376 return (HAMMER2_PBUFSIZE); 377 #if 0 378 } 379 #endif 380 } 381 382 /* 383 * Calculate the physical block size. pblksize <= lblksize. Primarily 384 * used to calculate a smaller physical block for the logical block 385 * containing the file EOF. 386 * 387 * Returns 0 if the requested base offset is beyond the file EOF. 388 */ 389 int 390 hammer2_calc_physical(hammer2_inode_t *ip, hammer2_key_t lbase) 391 { 392 int lblksize; 393 int pblksize; 394 int eofbytes; 395 396 lblksize = hammer2_calc_logical(ip, lbase, NULL, NULL); 397 if (lbase + lblksize <= ip->chain->data->ipdata.size) 398 return (lblksize); 399 if (lbase >= ip->chain->data->ipdata.size) 400 return (0); 401 eofbytes = (int)(ip->chain->data->ipdata.size - lbase); 402 pblksize = lblksize; 403 while (pblksize >= eofbytes && pblksize >= HAMMER2_MIN_ALLOC) 404 pblksize >>= 1; 405 pblksize <<= 1; 406 407 return (pblksize); 408 } 409 410 void 411 hammer2_update_time(uint64_t *timep) 412 { 413 struct timeval tv; 414 415 getmicrotime(&tv); 416 *timep = (unsigned long)tv.tv_sec * 1000000 + tv.tv_usec; 417 } 418