hammer2 - Start adding ioctl infrastructure, start writing hammer2 utility
[dragonfly.git] / sys / vfs / hammer2 / hammer2_inode.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
42 #include "hammer2.h"
43
44 /*
45  * Adding a ref to an inode is only legal if the inode already has at least
46  * one ref.
47  */
48 void
49 hammer2_inode_ref(hammer2_inode_t *ip)
50 {
51         hammer2_chain_ref(ip->hmp, &ip->chain);
52 }
53
54 /*
55  * Drop an inode reference, freeing the inode when the last reference goes
56  * away.
57  */
58 void
59 hammer2_inode_drop(hammer2_inode_t *ip)
60 {
61         hammer2_chain_drop(ip->hmp, &ip->chain);
62 }
63
64 /*
65  * Get the vnode associated with the given inode, allocating the vnode if
66  * necessary.
67  *
68  * Great care must be taken to avoid deadlocks and vnode acquisition/reclaim
69  * races.
70  *
71  * The vnode will be returned exclusively locked and referenced.  The
72  * reference on the vnode prevents it from being reclaimed.
73  *
74  * The inode (ip) must be referenced by the caller and not locked to avoid
75  * it getting ripped out from under us or deadlocked.
76  */
77 struct vnode *
78 hammer2_igetv(hammer2_inode_t *ip, int *errorp)
79 {
80         struct vnode *vp;
81         hammer2_mount_t *hmp;
82
83         hmp = ip->hmp;
84         *errorp = 0;
85
86         for (;;) {
87                 /*
88                  * Attempt to reuse an existing vnode assignment.  It is
89                  * possible to race a reclaim so the vget() may fail.  The
90                  * inode must be unlocked during the vget() to avoid a
91                  * deadlock against a reclaim.
92                  */
93                 vp = ip->vp;
94                 if (vp) {
95                         /*
96                          * Lock the inode and check for a reclaim race
97                          */
98                         hammer2_inode_lock_ex(ip);
99                         if (ip->vp != vp) {
100                                 hammer2_inode_unlock_ex(ip);
101                                 continue;
102                         }
103
104                         /*
105                          * Inode must be unlocked during the vget() to avoid
106                          * possible deadlocks, vnode is held to prevent
107                          * destruction during the vget().  The vget() can
108                          * still fail if we lost a reclaim race on the vnode.
109                          */
110                         vhold_interlocked(vp);
111                         hammer2_inode_unlock_ex(ip);
112                         if (vget(vp, LK_EXCLUSIVE)) {
113                                 vdrop(vp);
114                                 continue;
115                         }
116                         vdrop(vp);
117                         /* vp still locked and ref from vget */
118                         *errorp = 0;
119                         break;
120                 }
121
122                 /*
123                  * No vnode exists, allocate a new vnode.  Beware of
124                  * allocation races.  This function will return an
125                  * exclusively locked and referenced vnode.
126                  */
127                 *errorp = getnewvnode(VT_HAMMER2, H2TOMP(hmp), &vp, 0, 0);
128                 if (*errorp) {
129                         vp = NULL;
130                         break;
131                 }
132
133                 /*
134                  * Lock the inode and check for an allocation race.
135                  */
136                 hammer2_inode_lock_ex(ip);
137                 if (ip->vp != NULL) {
138                         vp->v_type = VBAD;
139                         vx_put(vp);
140                         hammer2_inode_unlock_ex(ip);
141                         continue;
142                 }
143
144                 switch (ip->ip_data.type) {
145                 case HAMMER2_OBJTYPE_DIRECTORY:
146                         vp->v_type = VDIR;
147                         break;
148                 case HAMMER2_OBJTYPE_REGFILE:
149                         vp->v_type = VREG;
150                         vinitvmio(vp, ip->ip_data.size,
151                                   HAMMER2_LBUFSIZE,
152                                   (int)ip->ip_data.size & HAMMER2_LBUFMASK);
153                         break;
154                 case HAMMER2_OBJTYPE_SOFTLINK:
155                         /*
156                          * XXX for now we are using the generic file_read
157                          * and file_write code so we need a buffer cache
158                          * association.
159                          */
160                         vp->v_type = VLNK;
161                         vinitvmio(vp, ip->ip_data.size,
162                                   HAMMER2_LBUFSIZE,
163                                   (int)ip->ip_data.size & HAMMER2_LBUFMASK);
164                         break;
165                 /* XXX FIFO */
166                 default:
167                         panic("hammer2: unhandled objtype %d",
168                               ip->ip_data.type);
169                         break;
170                 }
171
172                 if (ip == hmp->iroot)
173                         vsetflags(vp, VROOT);
174
175                 vp->v_data = ip;
176                 ip->vp = vp;
177                 hammer2_chain_ref(hmp, &ip->chain);     /* vp association */
178                 hammer2_inode_unlock_ex(ip);
179                 break;
180         }
181
182         /*
183          * Return non-NULL vp and *errorp == 0, or NULL vp and *errorp != 0.
184          */
185         if (hammer2_debug & 0x0002) {
186                 kprintf("igetv vp %p refs %d aux %d\n",
187                         vp, vp->v_sysref.refcnt, vp->v_auxrefs);
188         }
189         return (vp);
190 }
191
192 /*
193  * Create a new inode in the specified directory using the vattr to
194  * figure out the type of inode.
195  *
196  * If no error occurs the new inode with its chain locked is returned in
197  * *nipp, otherwise an error is returned and *nipp is set to NULL.
198  */
199 int
200 hammer2_inode_create(hammer2_mount_t *hmp,
201                      struct vattr *vap, struct ucred *cred,
202                      hammer2_inode_t *dip,
203                      const uint8_t *name, size_t name_len,
204                      hammer2_inode_t **nipp)
205 {
206         hammer2_chain_t *chain;
207         hammer2_chain_t *parent;
208         hammer2_inode_t *nip;
209         hammer2_key_t lhc;
210         int error;
211
212         lhc = hammer2_dirhash(name, name_len);
213
214         /*
215          * Locate the inode or indirect block to create the new
216          * entry in.  At the same time check for key collisions
217          * and iterate until we don't get one.
218          */
219         parent = &dip->chain;
220         hammer2_chain_lock(hmp, parent, HAMMER2_RESOLVE_ALWAYS);
221
222         error = 0;
223         while (error == 0) {
224                 chain = hammer2_chain_lookup(hmp, &parent, lhc, lhc, 0);
225                 if (chain == NULL)
226                         break;
227                 if ((lhc & HAMMER2_DIRHASH_LOMASK) == HAMMER2_DIRHASH_LOMASK)
228                         error = ENOSPC;
229                 hammer2_chain_unlock(hmp, chain);
230                 chain = NULL;
231                 ++lhc;
232         }
233         if (error == 0) {
234                 chain = hammer2_chain_create(hmp, parent, NULL, lhc, 0,
235                                              HAMMER2_BREF_TYPE_INODE,
236                                              HAMMER2_INODE_BYTES);
237                 if (chain == NULL)
238                         error = EIO;
239         }
240         hammer2_chain_unlock(hmp, parent);
241
242         /*
243          * Handle the error case
244          */
245         if (error) {
246                 KKASSERT(chain == NULL);
247                 *nipp = NULL;
248                 return (error);
249         }
250
251         /*
252          * Set up the new inode
253          */
254         nip = chain->u.ip;
255         *nipp = nip;
256
257         nip->ip_data.type = hammer2_get_obj_type(vap->va_type);
258         hammer2_voldata_lock(hmp);
259         nip->ip_data.inum = hmp->voldata.alloc_tid++;   /* XXX modify/lock */
260         hammer2_voldata_unlock(hmp);
261         nip->ip_data.version = HAMMER2_INODE_VERSION_ONE;
262         nip->ip_data.ctime = 0;
263         nip->ip_data.mtime = 0;
264         nip->ip_data.mode = vap->va_mode;
265         nip->ip_data.nlinks = 1;
266         /* uid, gid, etc */
267
268         /*
269          * Regular files and softlinks allow a small amount of data to be
270          * directly embedded in the inode.  This flag will be cleared if
271          * the size is extended past the embedded limit.
272          */
273         if (nip->ip_data.type == HAMMER2_OBJTYPE_REGFILE ||
274             nip->ip_data.type == HAMMER2_OBJTYPE_SOFTLINK) {
275                 nip->ip_data.op_flags |= HAMMER2_OPFLAG_DIRECTDATA;
276         }
277
278         KKASSERT(name_len < HAMMER2_INODE_MAXNAME);
279         bcopy(name, nip->ip_data.filename, name_len);
280         nip->ip_data.name_key = lhc;
281         nip->ip_data.name_len = name_len;
282
283         return (0);
284 }
285
286 /*
287  * Connect inode (ip) to the specified directory using the specified name.
288  * (ip) must be locked.
289  */
290 int
291 hammer2_inode_connect(hammer2_inode_t *dip, hammer2_inode_t *ip,
292                       const uint8_t *name, size_t name_len)
293 {
294         hammer2_mount_t *hmp = dip->hmp;
295         hammer2_chain_t *chain;
296         hammer2_chain_t *parent;
297         hammer2_key_t lhc;
298         int error;
299
300         lhc = hammer2_dirhash(name, name_len);
301
302         /*
303          * Locate the inode or indirect block to create the new
304          * entry in.  At the same time check for key collisions
305          * and iterate until we don't get one.
306          */
307         parent = &dip->chain;
308         hammer2_chain_lock(hmp, parent, HAMMER2_RESOLVE_ALWAYS);
309
310         error = 0;
311         while (error == 0) {
312                 chain = hammer2_chain_lookup(hmp, &parent, lhc, lhc, 0);
313                 if (chain == NULL)
314                         break;
315                 if ((lhc & HAMMER2_DIRHASH_LOMASK) == HAMMER2_DIRHASH_LOMASK)
316                         error = ENOSPC;
317                 hammer2_chain_unlock(hmp, chain);
318                 chain = NULL;
319                 ++lhc;
320         }
321
322         /*
323          * Passing a non-NULL chain to hammer2_chain_create() reconnects the
324          * existing chain instead of creating a new one.  The chain's bref
325          * will be properly updated.
326          */
327         if (error == 0) {
328                 chain = hammer2_chain_create(hmp, parent, &ip->chain, lhc, 0,
329                                              HAMMER2_BREF_TYPE_INODE /* n/a */,
330                                              HAMMER2_INODE_BYTES);   /* n/a */
331                 if (chain == NULL)
332                         error = EIO;
333         }
334         hammer2_chain_unlock(hmp, parent);
335
336         /*
337          * Handle the error case
338          */
339         if (error) {
340                 KKASSERT(chain == NULL);
341                 return (error);
342         }
343
344         /*
345          * Directory entries are inodes so if the name has changed we have
346          * to update the inode.
347          */
348         if (ip->ip_data.name_len != name_len ||
349             bcmp(ip->ip_data.filename, name, name_len) != 0) {
350                 hammer2_chain_modify(hmp, chain, 0);
351                 KKASSERT(name_len < HAMMER2_INODE_MAXNAME);
352                 bcopy(name, ip->ip_data.filename, name_len);
353                 ip->ip_data.name_key = lhc;
354                 ip->ip_data.name_len = name_len;
355         }
356         /*nip->ip_data.nlinks = 1;*/
357
358         return (0);
359 }
360
361 /*
362  * Create a hardlink forwarding entry (dip, name) to the specified (ip).
363  *
364  * This is one of the more complex implementations in HAMMER2.  The
365  * filesystem strictly updates its chains bottom-up in a copy-on-write
366  * fashion.  This makes hardlinks difficult to implement but we've come up
367  * with a dandy solution.
368  *
369  * When a file has more than one link the actual inode is created as a
370  * hidden directory entry (indexed by inode number) in a common parent of
371  * all hardlinks which reference the file.  The hardlinks in each directory
372  * are merely forwarding entries to the hidden inode.
373  *
374  * Implementation:
375  *
376  *      Most VOPs can be blissfully unaware of the forwarding entries.
377  *      nresolve, nlink, and remove code have to be forwarding-aware
378  *      in order to return the (ip/vp) for the actual file (and otherwise do
379  *      the right thing).
380  *
381  *      (1) If the ip we are linking to is a normal embedded inode (nlinks==1)
382  *          we have to replace the directory entry with a forwarding inode
383  *          and move the normal ip/vp to a hidden entry indexed by the inode
384  *          number in a common parent directory.
385  *
386  *      (2) If the ip we are linking to is already a hidden entry but is not
387  *          a common parent we have to move its entry to a common parent by
388  *          moving the entry upward.
389  *
390  *      (3) The trivial case is the entry is already hidden and already a
391  *          common parent.  We adjust nlinks for the entry and are done.
392  *          (this is the fall-through case).
393  */
394 int
395 hammer2_hardlink_create(hammer2_inode_t *ip, hammer2_inode_t *dip,
396                         const uint8_t *name, size_t name_len)
397 {
398         return ENOTSUP;
399 #if 0
400         hammer2_inode_t *nip;
401         hammer2_inode_t *xip;
402
403
404        hammer2_inode_t *nip;   /* hardlink forwarding inode */
405         error = hammer2_inode_create(hmp, NULL, ap->a_cred,
406                                      dip, name, name_len, &nip);
407         if (error) {
408                 KKASSERT(nip == NULL);
409                 return error;
410         }
411         KKASSERT(nip->ip_data.type == HAMMER2_OBJTYPE_HARDLINK);
412         hammer2_chain_modify(&nip->chain, 0);
413         nip->ip_data.inum = ip->ip_data.inum;
414         hammer2_chain_unlock(hmp, &nip->chain);
415         /
416 #endif
417 }
418
419 /*
420  * Calculate the allocation size for the file fragment straddling EOF
421  */
422 int
423 hammer2_inode_calc_alloc(hammer2_key_t filesize)
424 {
425         int frag = (int)filesize & HAMMER2_PBUFMASK;
426         int radix;
427
428         if (frag == 0)
429                 return(0);
430         for (radix = HAMMER2_MINALLOCRADIX; frag > (1 << radix); ++radix)
431                 ;
432         return (radix);
433 }