| Commit | Line | Data |
|---|---|---|
| 7cfa8da5 MD |
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/cdefs.h> | |
| 37 | #include <sys/param.h> | |
| 38 | #include <sys/systm.h> | |
| 39 | #include <sys/types.h> | |
| 40 | #include <sys/lock.h> | |
| 41 | #include <sys/uuid.h> | |
| 42 | ||
| 43 | #include "hammer2.h" | |
| 44 | ||
| 45 | /* | |
| 46 | * Adding a ref to an inode is only legal if the inode already has at least | |
| 47 | * one ref. | |
| 48 | */ | |
| 49 | void | |
| 50 | hammer2_inode_ref(hammer2_inode_t *ip) | |
| 51 | { | |
| 5c23d7f1 | 52 | hammer2_chain_ref(ip->hmp, &ip->chain); |
| 7cfa8da5 MD |
53 | } |
| 54 | ||
| 55 | /* | |
| 56 | * Drop an inode reference, freeing the inode when the last reference goes | |
| 57 | * away. | |
| 58 | */ | |
| 59 | void | |
| 60 | hammer2_inode_drop(hammer2_inode_t *ip) | |
| 61 | { | |
| 5c23d7f1 | 62 | hammer2_chain_drop(ip->hmp, &ip->chain); |
| 7cfa8da5 MD |
63 | } |
| 64 | ||
| 65 | /* | |
| 66 | * Get the vnode associated with the given inode, allocating the vnode if | |
| 67 | * necessary. | |
| 68 | * | |
| 69 | * Great care must be taken to avoid deadlocks and vnode acquisition/reclaim | |
| 70 | * races. | |
| 71 | * | |
| 72 | * The vnode will be returned exclusively locked and referenced. The | |
| 73 | * reference on the vnode prevents it from being reclaimed. | |
| 74 | * | |
| 75 | * The inode (ip) must be referenced by the caller and not locked to avoid | |
| 76 | * it getting ripped out from under us or deadlocked. | |
| 77 | */ | |
| 78 | struct vnode * | |
| 79 | hammer2_igetv(hammer2_inode_t *ip, int *errorp) | |
| 80 | { | |
| 81 | struct vnode *vp; | |
| 82 | hammer2_mount_t *hmp; | |
| 83 | ||
| 84 | hmp = ip->hmp; | |
| 85 | *errorp = 0; | |
| 86 | ||
| 87 | for (;;) { | |
| 88 | /* | |
| 89 | * Attempt to reuse an existing vnode assignment. It is | |
| 90 | * possible to race a reclaim so the vget() may fail. The | |
| 91 | * inode must be unlocked during the vget() to avoid a | |
| 92 | * deadlock against a reclaim. | |
| 93 | */ | |
| 94 | vp = ip->vp; | |
| 95 | if (vp) { | |
| 96 | /* | |
| 97 | * Lock the inode and check for a reclaim race | |
| 98 | */ | |
| 99 | hammer2_inode_lock_ex(ip); | |
| 100 | if (ip->vp != vp) { | |
| 101 | hammer2_inode_unlock_ex(ip); | |
| 102 | continue; | |
| 103 | } | |
| 104 | ||
| 105 | /* | |
| 106 | * Inode must be unlocked during the vget() to avoid | |
| 107 | * possible deadlocks, vnode is held to prevent | |
| 108 | * destruction during the vget(). The vget() can | |
| 109 | * still fail if we lost a reclaim race on the vnode. | |
| 110 | */ | |
| 111 | vhold_interlocked(vp); | |
| 112 | hammer2_inode_unlock_ex(ip); | |
| 113 | if (vget(vp, LK_EXCLUSIVE)) { | |
| 114 | vdrop(vp); | |
| 115 | continue; | |
| 116 | } | |
| 117 | vdrop(vp); | |
| 118 | /* vp still locked and ref from vget */ | |
| 119 | *errorp = 0; | |
| 120 | break; | |
| 121 | } | |
| 122 | ||
| 123 | /* | |
| 124 | * No vnode exists, allocate a new vnode. Beware of | |
| 125 | * allocation races. This function will return an | |
| 126 | * exclusively locked and referenced vnode. | |
| 127 | */ | |
| 128 | *errorp = getnewvnode(VT_HAMMER2, H2TOMP(hmp), &vp, 0, 0); | |
| 129 | if (*errorp) { | |
| 130 | vp = NULL; | |
| 131 | break; | |
| 132 | } | |
| 133 | ||
| 134 | /* | |
| 135 | * Lock the inode and check for an allocation race. | |
| 136 | */ | |
| 137 | hammer2_inode_lock_ex(ip); | |
| 138 | if (ip->vp != NULL) { | |
| 139 | vp->v_type = VBAD; | |
| 140 | vx_put(vp); | |
| 141 | hammer2_inode_unlock_ex(ip); | |
| 142 | continue; | |
| 143 | } | |
| 144 | ||
| 5c23d7f1 | 145 | switch (ip->ip_data.type) { |
| df9ea374 | 146 | case HAMMER2_OBJTYPE_DIRECTORY: |
| 7cfa8da5 MD |
147 | vp->v_type = VDIR; |
| 148 | break; | |
| df9ea374 | 149 | case HAMMER2_OBJTYPE_REGFILE: |
| 7cfa8da5 MD |
150 | vp->v_type = VREG; |
| 151 | vinitvmio(vp, 0, HAMMER2_LBUFSIZE, | |
| 5c23d7f1 | 152 | (int)ip->ip_data.size & HAMMER2_LBUFMASK); |
| 7cfa8da5 MD |
153 | break; |
| 154 | /* XXX FIFO */ | |
| 155 | default: | |
| 5c23d7f1 MD |
156 | panic("hammer2: unhandled objtype %d", |
| 157 | ip->ip_data.type); | |
| 7cfa8da5 MD |
158 | break; |
| 159 | } | |
| 160 | ||
| df9ea374 | 161 | if (ip == hmp->iroot) |
| 7cfa8da5 MD |
162 | vsetflags(vp, VROOT); |
| 163 | ||
| 164 | vp->v_data = ip; | |
| 165 | ip->vp = vp; | |
| 9c2e0de0 | 166 | hammer2_chain_ref(hmp, &ip->chain); /* vp association */ |
| 7cfa8da5 MD |
167 | hammer2_inode_unlock_ex(ip); |
| 168 | break; | |
| 169 | } | |
| 170 | ||
| 171 | /* | |
| 172 | * Return non-NULL vp and *errorp == 0, or NULL vp and *errorp != 0. | |
| 173 | */ | |
| 37aa19df MD |
174 | if (hammer2_debug & 0x0002) { |
| 175 | kprintf("igetv vp %p refs %d aux %d\n", | |
| 176 | vp, vp->v_sysref.refcnt, vp->v_auxrefs); | |
| 177 | } | |
| 7cfa8da5 MD |
178 | return (vp); |
| 179 | } | |
| 180 | ||
| 181 | /* | |
| 37494cab MD |
182 | * Create a new inode in the specified directory using the vattr to |
| 183 | * figure out the type of inode. | |
| 7cfa8da5 | 184 | * |
| db71f61f MD |
185 | * If no error occurs the new inode with its chain locked is returned in |
| 186 | * *nipp, otherwise an error is returned and *nipp is set to NULL. | |
| 7cfa8da5 | 187 | */ |
| 37494cab MD |
188 | int |
| 189 | hammer2_create_inode(hammer2_mount_t *hmp, | |
| 190 | struct vattr *vap, struct ucred *cred, | |
| 191 | hammer2_inode_t *dip, | |
| 192 | const uint8_t *name, size_t name_len, | |
| 193 | hammer2_inode_t **nipp) | |
| 7cfa8da5 | 194 | { |
| 37494cab MD |
195 | hammer2_chain_t *chain; |
| 196 | hammer2_chain_t *parent; | |
| 197 | hammer2_inode_t *nip; | |
| 198 | hammer2_key_t lhc; | |
| 199 | int error; | |
| 7cfa8da5 | 200 | |
| 37494cab | 201 | lhc = hammer2_dirhash(name, name_len); |
| 7cfa8da5 | 202 | |
| 37494cab MD |
203 | /* |
| 204 | * Locate the inode or indirect block to create the new | |
| 205 | * entry in. At the same time check for key collisions | |
| 206 | * and iterate until we don't get one. | |
| 207 | */ | |
| 208 | parent = &dip->chain; | |
| 209 | hammer2_chain_ref(hmp, parent); | |
| 210 | hammer2_chain_lock(hmp, parent); | |
| 7cfa8da5 | 211 | |
| 37494cab MD |
212 | error = 0; |
| 213 | while (error == 0) { | |
| c667909f | 214 | chain = hammer2_chain_lookup(hmp, &parent, lhc, lhc, 0); |
| 37494cab MD |
215 | if (chain == NULL) |
| 216 | break; | |
| 217 | if ((lhc & HAMMER2_DIRHASH_LOMASK) == HAMMER2_DIRHASH_LOMASK) | |
| 218 | error = ENOSPC; | |
| 219 | hammer2_chain_put(hmp, chain); | |
| 220 | chain = NULL; | |
| 221 | ++lhc; | |
| 222 | } | |
| 223 | if (error == 0) { | |
| 37aa19df | 224 | chain = hammer2_chain_create(hmp, parent, lhc, 0, |
| 37494cab MD |
225 | HAMMER2_BREF_TYPE_INODE, |
| 226 | HAMMER2_INODE_BYTES); | |
| 227 | if (chain == NULL) | |
| 228 | error = EIO; | |
| 229 | } | |
| 230 | hammer2_chain_put(hmp, parent); | |
| 7cfa8da5 | 231 | |
| 37494cab MD |
232 | /* |
| 233 | * Handle the error case | |
| 234 | */ | |
| 235 | if (error) { | |
| 236 | KKASSERT(chain == NULL); | |
| 237 | *nipp = NULL; | |
| 238 | return (error); | |
| 239 | } | |
| 7cfa8da5 | 240 | |
| 37494cab MD |
241 | /* |
| 242 | * Set up the new inode | |
| 243 | */ | |
| 244 | nip = chain->u.ip; | |
| 245 | *nipp = nip; | |
| 7cfa8da5 | 246 | |
| 37494cab MD |
247 | nip->ip_data.type = hammer2_get_obj_type(vap->va_type); |
| 248 | nip->ip_data.inum = hmp->voldata.alloc_tid++; /* XXX modify/lock */ | |
| 249 | nip->ip_data.version = HAMMER2_INODE_VERSION_ONE; | |
| 250 | nip->ip_data.ctime = 0; | |
| 251 | nip->ip_data.mtime = 0; | |
| 252 | nip->ip_data.mode = vap->va_mode; | |
| 253 | nip->ip_data.nlinks = 1; | |
| 254 | /* uid, gid, etc */ | |
| 232a50f9 | 255 | |
| 37494cab MD |
256 | KKASSERT(name_len < HAMMER2_INODE_MAXNAME); |
| 257 | bcopy(name, nip->ip_data.filename, name_len); | |
| 258 | nip->ip_data.name_key = lhc; | |
| 259 | nip->ip_data.name_len = name_len; | |
| 7cfa8da5 | 260 | |
| 37494cab | 261 | return (0); |
| 7cfa8da5 | 262 | } |