1/* $NetBSD: chfs_vnode.c,v 1.20 2021/12/07 22:13:56 andvar Exp $ */ 2 3/*- 4 * Copyright (c) 2010 Department of Software Engineering, 5 * University of Szeged, Hungary 6 * Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu> 7 * Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org> 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to The NetBSD Foundation 11 * by the Department of Software Engineering, University of Szeged, Hungary 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35#include "chfs.h" 36#include "chfs_inode.h" 37#include <sys/kauth.h> 38#include <sys/namei.h> 39#include <sys/uio.h> 40#include <sys/buf.h> 41 42#include <miscfs/genfs/genfs.h> 43 44/* chfs_vnode_lookup - lookup for a vnode */ 45static bool 46chfs_vnode_lookup_selector(void *ctx, struct vnode *vp) 47{ 48 ino_t *ino = ctx; 49 50 KASSERT(mutex_owned(vp->v_interlock)); 51 52 return (VTOI(vp) != NULL && VTOI(vp)->ino == *ino); 53} 54struct vnode * 55chfs_vnode_lookup(struct chfs_mount *chmp, ino_t vno) 56{ 57 struct vnode_iterator *marker; 58 struct vnode *vp; 59 60 vfs_vnode_iterator_init(chmp->chm_fsmp, &marker); 61 vp = vfs_vnode_iterator_next(marker, chfs_vnode_lookup_selector, &vno); 62 vfs_vnode_iterator_destroy(marker); 63 64 return vp; 65} 66 67/* chfs_readvnode - reads a vnode from the flash and setups its inode */ 68int 69chfs_readvnode(struct mount *mp, ino_t ino, struct vnode **vpp) 70{ 71 struct ufsmount* ump = VFSTOUFS(mp); 72 struct chfs_mount *chmp = ump->um_chfs; 73 struct chfs_vnode_cache *chvc; 74 struct chfs_flash_vnode *chfvn; 75 struct chfs_inode *ip; 76 int err; 77 char* buf; 78 size_t retlen, len; 79 struct vnode* vp = NULL; 80 dbg("readvnode | ino: %llu\n", (unsigned long long)ino); 81 82 len = sizeof(struct chfs_flash_vnode); 83 84 KASSERT(vpp != NULL); 85 86 if (vpp != NULL) { 87 vp = *vpp; 88 } 89 90 ip = VTOI(vp); 91 chvc = ip->chvc; 92 93 /* root node is in-memory only */ 94 if (chvc && ino != CHFS_ROOTINO) { 95 dbg("offset: %" PRIu32 ", lnr: %d\n", 96 CHFS_GET_OFS(chvc->v->nref_offset), chvc->v->nref_lnr); 97 98 KASSERT((void *)chvc != (void *)chvc->v); 99 100 /* reading */ 101 buf = kmem_alloc(len, KM_SLEEP); 102 err = chfs_read_leb(chmp, chvc->v->nref_lnr, buf, 103 CHFS_GET_OFS(chvc->v->nref_offset), len, &retlen); 104 if (err) { 105 kmem_free(buf, len); 106 return err; 107 } 108 if (retlen != len) { 109 chfs_err("Error reading vnode: read: %zu instead of: %zu\n", 110 len, retlen); 111 kmem_free(buf, len); 112 return EIO; 113 } 114 chfvn = (struct chfs_flash_vnode*)buf; 115 116 /* setup inode fields */ 117 chfs_set_vnode_size(vp, chfvn->dn_size); 118 ip->mode = chfvn->mode; 119 ip->ch_type = IFTOCHT(ip->mode); 120 vp->v_type = CHTTOVT(ip->ch_type); 121 ip->version = chfvn->version; 122 ip->uid = chfvn->uid; 123 ip->gid = chfvn->gid; 124 ip->atime = chfvn->atime; 125 ip->mtime = chfvn->mtime; 126 ip->ctime = chfvn->ctime; 127 128 kmem_free(buf, len); 129 } 130 131 132 *vpp = vp; 133 return 0; 134} 135 136/* 137 * chfs_readddirent - 138 * reads a directory entry from flash and adds it to its inode 139 */ 140int 141chfs_readdirent(struct mount *mp, struct chfs_node_ref *chnr, struct chfs_inode *pdir) 142{ 143 struct ufsmount *ump = VFSTOUFS(mp); 144 struct chfs_mount *chmp = ump->um_chfs; 145 struct chfs_flash_dirent_node chfdn; 146 struct chfs_dirent *fd; 147 size_t len = sizeof(struct chfs_flash_dirent_node); 148 size_t retlen; 149 int err = 0; 150 151 /* read flash_dirent_node */ 152 err = chfs_read_leb(chmp, chnr->nref_lnr, (char *)&chfdn, 153 CHFS_GET_OFS(chnr->nref_offset), len, &retlen); 154 if (err) { 155 return err; 156 } 157 if (retlen != len) { 158 chfs_err("Error reading vnode: read: %zu instead of: %zu\n", 159 retlen, len); 160 return EIO; 161 } 162 163 /* set fields of dirent */ 164 fd = chfs_alloc_dirent(chfdn.nsize + 1); 165 fd->version = chfdn.version; 166 fd->vno = chfdn.vno; 167 fd->type = chfdn.dtype; 168 fd->nsize = chfdn.nsize; 169 170 /* read the name of the dirent */ 171 err = chfs_read_leb(chmp, chnr->nref_lnr, fd->name, 172 CHFS_GET_OFS(chnr->nref_offset) + len, chfdn.nsize, &retlen); 173 if (err) { 174 return err; 175 } 176 177 if (retlen != chfdn.nsize) { 178 chfs_err("Error reading vnode: read: %zu instead of: %zu\n", 179 len, retlen); 180 return EIO; 181 } 182 183 fd->name[fd->nsize] = 0; 184 fd->nref = chnr; 185 186 /* add to inode */ 187 chfs_add_fd_to_inode(chmp, pdir, fd); 188 return 0; 189} 190 191/* chfs_makeinode - makes a new file and initializes its structures */ 192int 193chfs_makeinode(int mode, struct vnode *dvp, struct vnode **vpp, 194 struct componentname *cnp, enum vtype type) 195{ 196 struct chfs_inode *ip, *pdir; 197 struct vnode *vp; 198 struct ufsmount* ump = VFSTOUFS(dvp->v_mount); 199 struct chfs_mount* chmp = ump->um_chfs; 200 struct chfs_vnode_cache* chvc; 201 int error; 202 ino_t vno; 203 struct chfs_dirent *nfd; 204 205 dbg("makeinode\n"); 206 pdir = VTOI(dvp); 207 208 *vpp = NULL; 209 210 /* number of vnode will be the new maximum */ 211 vno = ++(chmp->chm_max_vno); 212 213 error = VFS_VGET(dvp->v_mount, vno, LK_EXCLUSIVE, &vp); 214 if (error) 215 return (error); 216 217 /* setup vnode cache */ 218 mutex_enter(&chmp->chm_lock_vnocache); 219 chvc = chfs_vnode_cache_get(chmp, vno); 220 221 chvc->pvno = pdir->ino; 222 chvc->vno_version = kmem_alloc(sizeof(uint64_t), KM_SLEEP); 223 *(chvc->vno_version) = 1; 224 if (type != VDIR) 225 chvc->nlink = 1; 226 else 227 chvc->nlink = 2; 228 chvc->state = VNO_STATE_CHECKEDABSENT; 229 mutex_exit(&chmp->chm_lock_vnocache); 230 231 /* setup inode */ 232 ip = VTOI(vp); 233 ip->ino = vno; 234 235 if (type == VDIR) 236 chfs_set_vnode_size(vp, 512); 237 else 238 chfs_set_vnode_size(vp, 0); 239 240 ip->uid = kauth_cred_geteuid(cnp->cn_cred); 241 ip->gid = kauth_cred_getegid(cnp->cn_cred); 242 ip->version = 1; 243 ip->iflag |= (IN_ACCESS | IN_CHANGE | IN_UPDATE); 244 245 ip->chvc = chvc; 246 ip->target = NULL; 247 248 ip->mode = mode; 249 vp->v_type = type; /* Rest init'd in chfs_loadvnode(). */ 250 ip->ch_type = VTTOCHT(vp->v_type); 251 252 /* authorize setting SGID if needed */ 253 if (ip->mode & ISGID) { 254 error = kauth_authorize_vnode(cnp->cn_cred, 255 KAUTH_VNODE_WRITE_SECURITY, vp, NULL, genfs_can_chmod(vp, 256 cnp->cn_cred, ip->uid, ip->gid, mode)); 257 if (error) 258 ip->mode &= ~ISGID; 259 } 260 261 /* write vnode information to the flash */ 262 chfs_update(vp, NULL, NULL, UPDATE_WAIT); 263 264 mutex_enter(&chmp->chm_lock_mountfields); 265 266 error = chfs_write_flash_vnode(chmp, ip, ALLOC_NORMAL); 267 if (error) { 268 mutex_exit(&chmp->chm_lock_mountfields); 269 vput(vp); 270 return error; 271 } 272 273 /* update parent's vnode information and write it to the flash */ 274 pdir->iflag |= (IN_ACCESS | IN_CHANGE | IN_MODIFY | IN_UPDATE); 275 chfs_update(dvp, NULL, NULL, UPDATE_WAIT); 276 277 error = chfs_write_flash_vnode(chmp, pdir, ALLOC_NORMAL); 278 if (error) { 279 mutex_exit(&chmp->chm_lock_mountfields); 280 vput(vp); 281 return error; 282 } 283 284 /* setup directory entry */ 285 nfd = chfs_alloc_dirent(cnp->cn_namelen + 1); 286 nfd->vno = ip->ino; 287 nfd->version = (++pdir->chvc->highest_version); 288 nfd->type = ip->ch_type; 289 nfd->nsize = cnp->cn_namelen; 290 memcpy(&(nfd->name), cnp->cn_nameptr, cnp->cn_namelen); 291 nfd->name[nfd->nsize] = 0; 292 nfd->nhash = hash32_buf(nfd->name, cnp->cn_namelen, HASH32_BUF_INIT); 293 294 /* write out */ 295 error = chfs_write_flash_dirent(chmp, pdir, ip, nfd, ip->ino, ALLOC_NORMAL); 296 if (error) { 297 mutex_exit(&chmp->chm_lock_mountfields); 298 vput(vp); 299 return error; 300 } 301 302 //TODO set parent's dir times 303 304 /* add dirent to parent */ 305 chfs_add_fd_to_inode(chmp, pdir, nfd); 306 307 pdir->chvc->nlink++; 308 309 mutex_exit(&chmp->chm_lock_mountfields); 310 311 VOP_UNLOCK(vp); 312 *vpp = vp; 313 cache_enter(dvp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags); 314 return (0); 315} 316 317/* chfs_set_vnode_size - updates size of vnode and also inode */ 318void 319chfs_set_vnode_size(struct vnode *vp, size_t size) 320{ 321 struct chfs_inode *ip; 322 323 KASSERT(vp != NULL); 324 325 ip = VTOI(vp); 326 KASSERT(ip != NULL); 327 328 ip->size = size; 329 vp->v_size = vp->v_writesize = size; 330 return; 331} 332 333/* 334 * chfs_change_size_free - updates free size 335 * "change" parameter is positive if we have to increase the size 336 * and negative if we have to decrease it 337 */ 338void 339chfs_change_size_free(struct chfs_mount *chmp, 340 struct chfs_eraseblock *cheb, int change) 341{ 342 KASSERT(mutex_owned(&chmp->chm_lock_sizes)); 343 KASSERT((int)(chmp->chm_free_size + change) >= 0); 344 KASSERT((int)(cheb->free_size + change) >= 0); 345 KASSERT((int)(cheb->free_size + change) <= chmp->chm_ebh->eb_size); 346 chmp->chm_free_size += change; 347 cheb->free_size += change; 348 return; 349} 350 351/* 352 * chfs_change_size_dirty - updates dirty size 353 * "change" parameter is positive if we have to increase the size 354 * and negative if we have to decrease it 355 */ 356void 357chfs_change_size_dirty(struct chfs_mount *chmp, 358 struct chfs_eraseblock *cheb, int change) 359{ 360 KASSERT(mutex_owned(&chmp->chm_lock_sizes)); 361 KASSERT((int)(chmp->chm_dirty_size + change) >= 0); 362 KASSERT((int)(cheb->dirty_size + change) >= 0); 363 KASSERT((int)(cheb->dirty_size + change) <= chmp->chm_ebh->eb_size); 364 chmp->chm_dirty_size += change; 365 cheb->dirty_size += change; 366 return; 367} 368 369/* 370 * chfs_change_size_unchecked - updates unchecked size 371 * "change" parameter is positive if we have to increase the size 372 * and negative if we have to decrease it 373 */ 374void 375chfs_change_size_unchecked(struct chfs_mount *chmp, 376 struct chfs_eraseblock *cheb, int change) 377{ 378 KASSERT(mutex_owned(&chmp->chm_lock_sizes)); 379 KASSERT((int)(chmp->chm_unchecked_size + change) >= 0); 380 KASSERT((int)(cheb->unchecked_size + change) >= 0); 381 KASSERT((int)(cheb->unchecked_size + change) <= chmp->chm_ebh->eb_size); 382 chmp->chm_unchecked_size += change; 383 cheb->unchecked_size += change; 384 return; 385} 386 387/* 388 * chfs_change_size_used - updates used size 389 * "change" parameter is positive if we have to increase the size 390 * and negative if we have to decrease it 391 */ 392void 393chfs_change_size_used(struct chfs_mount *chmp, 394 struct chfs_eraseblock *cheb, int change) 395{ 396 KASSERT(mutex_owned(&chmp->chm_lock_sizes)); 397 KASSERT((int)(chmp->chm_used_size + change) >= 0); 398 KASSERT((int)(cheb->used_size + change) >= 0); 399 KASSERT((int)(cheb->used_size + change) <= chmp->chm_ebh->eb_size); 400 chmp->chm_used_size += change; 401 cheb->used_size += change; 402 return; 403} 404 405/* 406 * chfs_change_size_wasted - updates wasted size 407 * "change" parameter is positive if we have to increase the size 408 * and negative if we have to decrease it 409 */ 410void 411chfs_change_size_wasted(struct chfs_mount *chmp, 412 struct chfs_eraseblock *cheb, int change) 413{ 414 KASSERT(mutex_owned(&chmp->chm_lock_sizes)); 415 KASSERT((int)(chmp->chm_wasted_size + change) >= 0); 416 KASSERT((int)(cheb->wasted_size + change) >= 0); 417 KASSERT((int)(cheb->wasted_size + change) <= chmp->chm_ebh->eb_size); 418 chmp->chm_wasted_size += change; 419 cheb->wasted_size += change; 420 return; 421} 422 423