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 #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 * HAMMER2 inode locks 47 * 48 * HAMMER2 offers shared locks, update locks, and exclusive locks on inodes. 49 * 50 * Shared locks allow concurrent access to an inode's fields, but exclude 51 * access by concurrent exclusive locks. 52 * 53 * Update locks are interesting -- an update lock will be taken after all 54 * shared locks on an inode are released, but once it is in place, shared 55 * locks may proceed. The update field is signalled by a busy flag in the 56 * inode. Only one update lock may be in place at a given time on an inode. 57 * 58 * Exclusive locks prevent concurrent access to the inode. 59 * 60 * XXX: What do we use each for? How is visibility to the inode controlled? 61 */ 62 63 64 void 65 hammer2_inode_lock_ex(hammer2_inode_t *ip) 66 { 67 hammer2_chain_lock(ip->hmp, &ip->chain, HAMMER2_RESOLVE_ALWAYS); 68 } 69 70 void 71 hammer2_inode_unlock_ex(hammer2_inode_t *ip) 72 { 73 hammer2_chain_unlock(ip->hmp, &ip->chain); 74 } 75 76 void 77 hammer2_inode_lock_sh(hammer2_inode_t *ip) 78 { 79 KKASSERT(ip->chain.refs > 0); 80 hammer2_chain_lock(ip->hmp, &ip->chain, HAMMER2_RESOLVE_ALWAYS | 81 HAMMER2_RESOLVE_SHARED); 82 } 83 84 void 85 hammer2_inode_unlock_sh(hammer2_inode_t *ip) 86 { 87 hammer2_chain_unlock(ip->hmp, &ip->chain); 88 } 89 90 #if 0 91 /* 92 * Soft-busy an inode. 93 * 94 * The inode must be exclusively locked while soft-busying or soft-unbusying 95 * an inode. Once busied or unbusied the caller can release the lock. 96 */ 97 void 98 hammer2_inode_busy(hammer2_inode_t *ip) 99 { 100 if (ip->chain.busy++ == 0) 101 hammer2_chain_ref(ip->hmp, &ip->chain, 0); 102 } 103 104 void 105 hammer2_inode_unbusy(hammer2_inode_t *ip) 106 { 107 if (--ip->chain.busy == 0) 108 hammer2_chain_drop(ip->hmp, &ip->chain); 109 } 110 111 #endif 112 113 /* 114 * Mount-wide locks 115 */ 116 117 void 118 hammer2_mount_exlock(hammer2_mount_t *hmp) 119 { 120 ccms_thread_lock(&hmp->vchain.cst, CCMS_STATE_EXCLUSIVE); 121 } 122 123 void 124 hammer2_mount_shlock(hammer2_mount_t *hmp) 125 { 126 ccms_thread_lock(&hmp->vchain.cst, CCMS_STATE_SHARED); 127 } 128 129 void 130 hammer2_mount_unlock(hammer2_mount_t *hmp) 131 { 132 ccms_thread_unlock(&hmp->vchain.cst); 133 } 134 135 void 136 hammer2_voldata_lock(hammer2_mount_t *hmp) 137 { 138 lockmgr(&hmp->voldatalk, LK_EXCLUSIVE); 139 } 140 141 void 142 hammer2_voldata_unlock(hammer2_mount_t *hmp) 143 { 144 lockmgr(&hmp->voldatalk, LK_RELEASE); 145 } 146 147 /* 148 * Return the directory entry type for an inode 149 */ 150 int 151 hammer2_get_dtype(hammer2_inode_t *ip) 152 { 153 uint8_t type; 154 155 if ((type = ip->ip_data.type) == HAMMER2_OBJTYPE_HARDLINK) 156 type = ip->ip_data.target_type; 157 158 switch(type) { 159 case HAMMER2_OBJTYPE_UNKNOWN: 160 return (DT_UNKNOWN); 161 case HAMMER2_OBJTYPE_DIRECTORY: 162 return (DT_DIR); 163 case HAMMER2_OBJTYPE_REGFILE: 164 return (DT_REG); 165 case HAMMER2_OBJTYPE_FIFO: 166 return (DT_FIFO); 167 case HAMMER2_OBJTYPE_CDEV: /* not supported */ 168 return (DT_CHR); 169 case HAMMER2_OBJTYPE_BDEV: /* not supported */ 170 return (DT_BLK); 171 case HAMMER2_OBJTYPE_SOFTLINK: 172 return (DT_LNK); 173 case HAMMER2_OBJTYPE_HARDLINK: /* (never directly associated w/vp) */ 174 return (DT_UNKNOWN); 175 case HAMMER2_OBJTYPE_SOCKET: 176 return (DT_SOCK); 177 case HAMMER2_OBJTYPE_WHITEOUT: /* not supported */ 178 return (DT_UNKNOWN); 179 default: 180 return (DT_UNKNOWN); 181 } 182 /* not reached */ 183 } 184 185 /* 186 * Return the directory entry type for an inode 187 */ 188 int 189 hammer2_get_vtype(hammer2_inode_t *ip) 190 { 191 switch(ip->ip_data.type) { 192 case HAMMER2_OBJTYPE_UNKNOWN: 193 return (VBAD); 194 case HAMMER2_OBJTYPE_DIRECTORY: 195 return (VDIR); 196 case HAMMER2_OBJTYPE_REGFILE: 197 return (VREG); 198 case HAMMER2_OBJTYPE_FIFO: 199 return (VFIFO); 200 case HAMMER2_OBJTYPE_CDEV: /* not supported */ 201 return (VCHR); 202 case HAMMER2_OBJTYPE_BDEV: /* not supported */ 203 return (VBLK); 204 case HAMMER2_OBJTYPE_SOFTLINK: 205 return (VLNK); 206 case HAMMER2_OBJTYPE_HARDLINK: /* XXX */ 207 return (VBAD); 208 case HAMMER2_OBJTYPE_SOCKET: 209 return (VSOCK); 210 case HAMMER2_OBJTYPE_WHITEOUT: /* not supported */ 211 return (DT_UNKNOWN); 212 default: 213 return (DT_UNKNOWN); 214 } 215 /* not reached */ 216 } 217 218 u_int8_t 219 hammer2_get_obj_type(enum vtype vtype) 220 { 221 switch(vtype) { 222 case VDIR: 223 return(HAMMER2_OBJTYPE_DIRECTORY); 224 case VREG: 225 return(HAMMER2_OBJTYPE_REGFILE); 226 case VFIFO: 227 return(HAMMER2_OBJTYPE_FIFO); 228 case VSOCK: 229 return(HAMMER2_OBJTYPE_SOCKET); 230 case VCHR: 231 return(HAMMER2_OBJTYPE_CDEV); 232 case VBLK: 233 return(HAMMER2_OBJTYPE_BDEV); 234 case VLNK: 235 return(HAMMER2_OBJTYPE_SOFTLINK); 236 default: 237 return(HAMMER2_OBJTYPE_UNKNOWN); 238 } 239 /* not reached */ 240 } 241 242 /* 243 * Convert a hammer2 64-bit time to a timespec. 244 */ 245 void 246 hammer2_time_to_timespec(u_int64_t xtime, struct timespec *ts) 247 { 248 ts->tv_sec = (unsigned long)(xtime / 1000000); 249 ts->tv_nsec = (unsigned int)(xtime % 1000000) * 1000L; 250 } 251 252 u_int64_t 253 hammer2_timespec_to_time(struct timespec *ts) 254 { 255 u_int64_t xtime; 256 257 xtime = (unsigned)(ts->tv_nsec / 1000) + 258 (unsigned long)ts->tv_sec * 1000000ULL; 259 return(xtime); 260 } 261 262 /* 263 * Convert a uuid to a unix uid or gid 264 */ 265 u_int32_t 266 hammer2_to_unix_xid(uuid_t *uuid) 267 { 268 return(*(u_int32_t *)&uuid->node[2]); 269 } 270 271 void 272 hammer2_guid_to_uuid(uuid_t *uuid, u_int32_t guid) 273 { 274 bzero(uuid, sizeof(*uuid)); 275 *(u_int32_t *)&uuid->node[2] = guid; 276 } 277 278 /* 279 * Borrow HAMMER1's directory hash algorithm #1 with a few modifications. 280 * The filename is split into fields which are hashed separately and then 281 * added together. 282 * 283 * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets 284 * it to 0), this is because bit63=0 is used for hidden hardlinked inodes. 285 * (This means we do not need to do a 0-check/or-with-0x100000000 either). 286 * 287 * Also, the iscsi crc code is used instead of the old crc32 code. 288 */ 289 hammer2_key_t 290 hammer2_dirhash(const unsigned char *name, size_t len) 291 { 292 const unsigned char *aname = name; 293 uint32_t crcx; 294 uint64_t key; 295 size_t i; 296 size_t j; 297 298 key = 0; 299 300 /* 301 * m32 302 */ 303 crcx = 0; 304 for (i = j = 0; i < len; ++i) { 305 if (aname[i] == '.' || 306 aname[i] == '-' || 307 aname[i] == '_' || 308 aname[i] == '~') { 309 if (i != j) 310 crcx += hammer2_icrc32(aname + j, i - j); 311 j = i + 1; 312 } 313 } 314 if (i != j) 315 crcx += hammer2_icrc32(aname + j, i - j); 316 317 /* 318 * The directory hash utilizes the top 32 bits of the 64-bit key. 319 * Bit 63 must be set to 1. 320 */ 321 crcx |= 0x80000000U; 322 key |= (uint64_t)crcx << 32; 323 324 /* 325 * l16 - crc of entire filename 326 * 327 * This crc reduces degenerate hash collision conditions 328 */ 329 crcx = hammer2_icrc32(aname, len); 330 crcx = crcx ^ (crcx << 16); 331 key |= crcx & 0xFFFF0000U; 332 333 /* 334 * Set bit 15. This allows readdir to strip bit 63 so a positive 335 * 64-bit cookie/offset can always be returned, and still guarantee 336 * that the values 0x0000-0x7FFF are available for artificial entries. 337 * ('.' and '..'). 338 */ 339 key |= 0x8000U; 340 341 return (key); 342 } 343 344 /* 345 * Return the power-of-2 radix greater or equal to 346 * the specified number of bytes. 347 * 348 * Always returns at least HAMMER2_MIN_RADIX (2^6). 349 */ 350 int 351 hammer2_bytes_to_radix(size_t bytes) 352 { 353 int radix; 354 355 if (bytes < HAMMER2_MIN_ALLOC) 356 bytes = HAMMER2_MIN_ALLOC; 357 if (bytes == HAMMER2_PBUFSIZE) 358 radix = HAMMER2_PBUFRADIX; 359 else if (bytes >= 1024) 360 radix = 10; 361 else 362 radix = HAMMER2_MIN_RADIX; 363 364 while (((size_t)1 << radix) < bytes) 365 ++radix; 366 return (radix); 367 } 368 369 int 370 hammer2_calc_logical(hammer2_inode_t *ip, hammer2_off_t uoff, 371 hammer2_key_t *lbasep, hammer2_key_t *leofp) 372 { 373 int radix; 374 375 *lbasep = uoff & ~HAMMER2_PBUFMASK64; 376 *leofp = ip->ip_data.size & ~HAMMER2_PBUFMASK64; 377 KKASSERT(*lbasep <= *leofp); 378 if (*lbasep == *leofp /*&& *leofp < 1024 * 1024*/) { 379 radix = hammer2_bytes_to_radix( 380 (size_t)(ip->ip_data.size - *leofp)); 381 if (radix < HAMMER2_MINALLOCRADIX) 382 radix = HAMMER2_MINALLOCRADIX; 383 *leofp += 1U << radix; 384 return (1U << radix); 385 } else { 386 return (HAMMER2_PBUFSIZE); 387 } 388 } 389 390 void 391 hammer2_update_time(uint64_t *timep) 392 { 393 struct timeval tv; 394 395 getmicrotime(&tv); 396 *timep = (unsigned long)tv.tv_sec * 1000000 + tv.tv_usec; 397 } 398