Merge branch 'vendor/EXPAT'
[dragonfly.git] / sys / vfs / hammer2 / hammer2_subr.c
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 and exclusive locks on inodes.
49  *
50  * An inode's ip->chain pointer is resolved and stable while an inode is
51  * locked, and can be cleaned out at any time (become NULL) when an inode
52  * is not locked.
53  *
54  * The underlying chain is also locked and returned.
55  *
56  * NOTE: We don't combine the inode/chain lock because putting away an
57  *       inode would otherwise confuse multiple lock holders of the inode.
58  */
59 hammer2_chain_t *
60 hammer2_inode_lock_ex(hammer2_inode_t *ip)
61 {
62         hammer2_chain_t *chain;
63
64         hammer2_inode_ref(ip);
65         ccms_thread_lock(&ip->topo_cst, CCMS_STATE_EXCLUSIVE);
66
67         chain = ip->chain;
68         KKASSERT(chain != NULL);        /* for now */
69         hammer2_chain_lock(ip->hmp, chain, HAMMER2_RESOLVE_ALWAYS);
70
71         return (chain);
72 }
73
74 void
75 hammer2_inode_unlock_ex(hammer2_inode_t *ip, hammer2_chain_t *chain)
76 {
77         /*
78          * XXX this will catch parent directories too which we don't
79          *     really want.
80          */
81         if (ip->chain && (ip->chain->flags & (HAMMER2_CHAIN_MODIFIED |
82                                               HAMMER2_CHAIN_SUBMODIFIED))) {
83                 atomic_set_int(&ip->flags, HAMMER2_INODE_MODIFIED);
84         }
85         if (chain)
86                 hammer2_chain_unlock(ip->hmp, chain);
87         ccms_thread_unlock(&ip->topo_cst);
88         hammer2_inode_drop(ip);
89 }
90
91 /*
92  * NOTE: We don't combine the inode/chain lock because putting away an
93  *       inode would otherwise confuse multiple lock holders of the inode.
94  *
95  *       Shared locks are especially sensitive to having too many shared
96  *       lock counts (from the same thread) on certain paths which might
97  *       need to upgrade them.  Only one count of a shared lock can be
98  *       upgraded.
99  */
100 hammer2_chain_t *
101 hammer2_inode_lock_sh(hammer2_inode_t *ip)
102 {
103         hammer2_chain_t *chain;
104
105         hammer2_inode_ref(ip);
106         ccms_thread_lock(&ip->topo_cst, CCMS_STATE_SHARED);
107
108         chain = ip->chain;
109         KKASSERT(chain != NULL);        /* for now */
110         hammer2_chain_lock(ip->hmp, chain, HAMMER2_RESOLVE_ALWAYS |
111                                            HAMMER2_RESOLVE_SHARED);
112         return (chain);
113 }
114
115 void
116 hammer2_inode_unlock_sh(hammer2_inode_t *ip, hammer2_chain_t *chain)
117 {
118         if (chain)
119                 hammer2_chain_unlock(ip->hmp, chain);
120         ccms_thread_unlock(&ip->topo_cst);
121         hammer2_inode_drop(ip);
122 }
123
124 ccms_state_t
125 hammer2_inode_lock_temp_release(hammer2_inode_t *ip)
126 {
127         return(ccms_thread_lock_temp_release(&ip->topo_cst));
128 }
129
130 ccms_state_t
131 hammer2_inode_lock_upgrade(hammer2_inode_t *ip)
132 {
133         return(ccms_thread_lock_upgrade(&ip->topo_cst));
134 }
135
136 void
137 hammer2_inode_lock_restore(hammer2_inode_t *ip, ccms_state_t ostate)
138 {
139         ccms_thread_lock_restore(&ip->topo_cst, ostate);
140 }
141
142 /*
143  * Mount-wide locks
144  */
145
146 void
147 hammer2_mount_exlock(hammer2_mount_t *hmp)
148 {
149         ccms_thread_lock(&hmp->vchain.cst, CCMS_STATE_EXCLUSIVE);
150 }
151
152 void
153 hammer2_mount_shlock(hammer2_mount_t *hmp)
154 {
155         ccms_thread_lock(&hmp->vchain.cst, CCMS_STATE_SHARED);
156 }
157
158 void
159 hammer2_mount_unlock(hammer2_mount_t *hmp)
160 {
161         ccms_thread_unlock(&hmp->vchain.cst);
162 }
163
164 void
165 hammer2_voldata_lock(hammer2_mount_t *hmp)
166 {
167         lockmgr(&hmp->voldatalk, LK_EXCLUSIVE);
168 }
169
170 void
171 hammer2_voldata_unlock(hammer2_mount_t *hmp)
172 {
173         lockmgr(&hmp->voldatalk, LK_RELEASE);
174 }
175
176 /*
177  * Return the directory entry type for an inode.
178  *
179  * ip must be locked sh/ex.
180  */
181 int
182 hammer2_get_dtype(hammer2_chain_t *chain)
183 {
184         uint8_t type;
185
186         KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE);
187
188         if ((type = chain->data->ipdata.type) == HAMMER2_OBJTYPE_HARDLINK)
189                 type = chain->data->ipdata.target_type;
190
191         switch(type) {
192         case HAMMER2_OBJTYPE_UNKNOWN:
193                 return (DT_UNKNOWN);
194         case HAMMER2_OBJTYPE_DIRECTORY:
195                 return (DT_DIR);
196         case HAMMER2_OBJTYPE_REGFILE:
197                 return (DT_REG);
198         case HAMMER2_OBJTYPE_FIFO:
199                 return (DT_FIFO);
200         case HAMMER2_OBJTYPE_CDEV:      /* not supported */
201                 return (DT_CHR);
202         case HAMMER2_OBJTYPE_BDEV:      /* not supported */
203                 return (DT_BLK);
204         case HAMMER2_OBJTYPE_SOFTLINK:
205                 return (DT_LNK);
206         case HAMMER2_OBJTYPE_HARDLINK:  /* (never directly associated w/vp) */
207                 return (DT_UNKNOWN);
208         case HAMMER2_OBJTYPE_SOCKET:
209                 return (DT_SOCK);
210         case HAMMER2_OBJTYPE_WHITEOUT:  /* not supported */
211                 return (DT_UNKNOWN);
212         default:
213                 return (DT_UNKNOWN);
214         }
215         /* not reached */
216 }
217
218 /*
219  * Return the directory entry type for an inode
220  */
221 int
222 hammer2_get_vtype(hammer2_chain_t *chain)
223 {
224         KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE);
225
226         switch(chain->data->ipdata.type) {
227         case HAMMER2_OBJTYPE_UNKNOWN:
228                 return (VBAD);
229         case HAMMER2_OBJTYPE_DIRECTORY:
230                 return (VDIR);
231         case HAMMER2_OBJTYPE_REGFILE:
232                 return (VREG);
233         case HAMMER2_OBJTYPE_FIFO:
234                 return (VFIFO);
235         case HAMMER2_OBJTYPE_CDEV:      /* not supported */
236                 return (VCHR);
237         case HAMMER2_OBJTYPE_BDEV:      /* not supported */
238                 return (VBLK);
239         case HAMMER2_OBJTYPE_SOFTLINK:
240                 return (VLNK);
241         case HAMMER2_OBJTYPE_HARDLINK:  /* XXX */
242                 return (VBAD);
243         case HAMMER2_OBJTYPE_SOCKET:
244                 return (VSOCK);
245         case HAMMER2_OBJTYPE_WHITEOUT:  /* not supported */
246                 return (DT_UNKNOWN);
247         default:
248                 return (DT_UNKNOWN);
249         }
250         /* not reached */
251 }
252
253 u_int8_t
254 hammer2_get_obj_type(enum vtype vtype)
255 {
256         switch(vtype) {
257         case VDIR:
258                 return(HAMMER2_OBJTYPE_DIRECTORY);
259         case VREG:
260                 return(HAMMER2_OBJTYPE_REGFILE);
261         case VFIFO:
262                 return(HAMMER2_OBJTYPE_FIFO);
263         case VSOCK:
264                 return(HAMMER2_OBJTYPE_SOCKET);
265         case VCHR:
266                 return(HAMMER2_OBJTYPE_CDEV);
267         case VBLK:
268                 return(HAMMER2_OBJTYPE_BDEV);
269         case VLNK:
270                 return(HAMMER2_OBJTYPE_SOFTLINK);
271         default:
272                 return(HAMMER2_OBJTYPE_UNKNOWN);
273         }
274         /* not reached */
275 }
276
277 /*
278  * Convert a hammer2 64-bit time to a timespec.
279  */
280 void
281 hammer2_time_to_timespec(u_int64_t xtime, struct timespec *ts)
282 {
283         ts->tv_sec = (unsigned long)(xtime / 1000000);
284         ts->tv_nsec = (unsigned int)(xtime % 1000000) * 1000L;
285 }
286
287 u_int64_t
288 hammer2_timespec_to_time(struct timespec *ts)
289 {
290         u_int64_t xtime;
291
292         xtime = (unsigned)(ts->tv_nsec / 1000) +
293                 (unsigned long)ts->tv_sec * 1000000ULL;
294         return(xtime);
295 }
296
297 /*
298  * Convert a uuid to a unix uid or gid
299  */
300 u_int32_t
301 hammer2_to_unix_xid(uuid_t *uuid)
302 {
303         return(*(u_int32_t *)&uuid->node[2]);
304 }
305
306 void
307 hammer2_guid_to_uuid(uuid_t *uuid, u_int32_t guid)
308 {
309         bzero(uuid, sizeof(*uuid));
310         *(u_int32_t *)&uuid->node[2] = guid;
311 }
312
313 /*
314  * Borrow HAMMER1's directory hash algorithm #1 with a few modifications.
315  * The filename is split into fields which are hashed separately and then
316  * added together.
317  *
318  * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets
319  * it to 0), this is because bit63=0 is used for hidden hardlinked inodes.
320  * (This means we do not need to do a 0-check/or-with-0x100000000 either).
321  *
322  * Also, the iscsi crc code is used instead of the old crc32 code.
323  */
324 hammer2_key_t
325 hammer2_dirhash(const unsigned char *name, size_t len)
326 {
327         const unsigned char *aname = name;
328         uint32_t crcx;
329         uint64_t key;
330         size_t i;
331         size_t j;
332
333         key = 0;
334
335         /*
336          * m32
337          */
338         crcx = 0;
339         for (i = j = 0; i < len; ++i) {
340                 if (aname[i] == '.' ||
341                     aname[i] == '-' ||
342                     aname[i] == '_' ||
343                     aname[i] == '~') {
344                         if (i != j)
345                                 crcx += hammer2_icrc32(aname + j, i - j);
346                         j = i + 1;
347                 }
348         }
349         if (i != j)
350                 crcx += hammer2_icrc32(aname + j, i - j);
351
352         /*
353          * The directory hash utilizes the top 32 bits of the 64-bit key.
354          * Bit 63 must be set to 1.
355          */
356         crcx |= 0x80000000U;
357         key |= (uint64_t)crcx << 32;
358
359         /*
360          * l16 - crc of entire filename
361          *
362          * This crc reduces degenerate hash collision conditions
363          */
364         crcx = hammer2_icrc32(aname, len);
365         crcx = crcx ^ (crcx << 16);
366         key |= crcx & 0xFFFF0000U;
367
368         /*
369          * Set bit 15.  This allows readdir to strip bit 63 so a positive
370          * 64-bit cookie/offset can always be returned, and still guarantee
371          * that the values 0x0000-0x7FFF are available for artificial entries.
372          * ('.' and '..').
373          */
374         key |= 0x8000U;
375
376         return (key);
377 }
378
379 /*
380  * Return the power-of-2 radix greater or equal to
381  * the specified number of bytes.
382  *
383  * Always returns at least the minimum media allocation
384  * size radix, HAMMER2_MIN_RADIX (10), which is 1KB.
385  */
386 int
387 hammer2_allocsize(size_t bytes)
388 {
389         int radix;
390
391         if (bytes < HAMMER2_MIN_ALLOC)
392                 bytes = HAMMER2_MIN_ALLOC;
393         if (bytes == HAMMER2_PBUFSIZE)
394                 radix = HAMMER2_PBUFRADIX;
395         else if (bytes >= 16384)
396                 radix = 14;
397         else if (bytes >= 1024)
398                 radix = 10;
399         else
400                 radix = HAMMER2_MIN_RADIX;
401
402         while (((size_t)1 << radix) < bytes)
403                 ++radix;
404         return (radix);
405 }
406
407 /*
408  * ip must be locked sh/ex
409  */
410 int
411 hammer2_calc_logical(hammer2_inode_t *ip, hammer2_off_t uoff,
412                      hammer2_key_t *lbasep, hammer2_key_t *leofp)
413 {
414         hammer2_inode_data_t *ipdata = &ip->chain->data->ipdata;
415         int radix;
416
417         *lbasep = uoff & ~HAMMER2_PBUFMASK64;
418         *leofp = ipdata->size & ~HAMMER2_PBUFMASK64;
419         KKASSERT(*lbasep <= *leofp);
420         if (*lbasep == *leofp /*&& *leofp < 1024 * 1024*/) {
421                 radix = hammer2_allocsize((size_t)(ipdata->size - *leofp));
422                 if (radix < HAMMER2_MINALLOCRADIX)
423                         radix = HAMMER2_MINALLOCRADIX;
424                 *leofp += 1U << radix;
425                 return (1U << radix);
426         } else {
427                 return (HAMMER2_PBUFSIZE);
428         }
429 }
430
431 void
432 hammer2_update_time(uint64_t *timep)
433 {
434         struct timeval tv;
435
436         getmicrotime(&tv);
437         *timep = (unsigned long)tv.tv_sec * 1000000 + tv.tv_usec;
438 }