1235537Sgber/*- 2235537Sgber * Copyright (c) 2010-2012 Semihalf 3235537Sgber * Copyright (c) 2008, 2009 Reinoud Zandijk 4235537Sgber * All rights reserved. 5235537Sgber * 6235537Sgber * Redistribution and use in source and binary forms, with or without 7235537Sgber * modification, are permitted provided that the following conditions 8235537Sgber * are met: 9235537Sgber * 1. Redistributions of source code must retain the above copyright 10235537Sgber * notice, this list of conditions and the following disclaimer. 11235537Sgber * 2. Redistributions in binary form must reproduce the above copyright 12235537Sgber * notice, this list of conditions and the following disclaimer in the 13235537Sgber * documentation and/or other materials provided with the distribution. 14235537Sgber * 15235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16235537Sgber * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17235537Sgber * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18235537Sgber * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19235537Sgber * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20235537Sgber * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21235537Sgber * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22235537Sgber * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23235537Sgber * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24235537Sgber * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25235537Sgber * 26235537Sgber * From: NetBSD: nilfs_subr.c,v 1.4 2009/07/29 17:06:57 reinoud 27235537Sgber */ 28235537Sgber 29235537Sgber#include <sys/cdefs.h> 30235537Sgber__FBSDID("$FreeBSD$"); 31235537Sgber 32235537Sgber#include <sys/param.h> 33235537Sgber#include <sys/systm.h> 34235537Sgber#include <sys/namei.h> 35235537Sgber#include <sys/kernel.h> 36235537Sgber#include <sys/stat.h> 37235537Sgber#include <sys/buf.h> 38235537Sgber#include <sys/bio.h> 39235537Sgber#include <sys/proc.h> 40235537Sgber#include <sys/mount.h> 41235537Sgber#include <sys/vnode.h> 42235537Sgber#include <sys/signalvar.h> 43235537Sgber#include <sys/malloc.h> 44235537Sgber#include <sys/dirent.h> 45235537Sgber#include <sys/lockf.h> 46235537Sgber 47235537Sgber#include <vm/vm.h> 48235537Sgber#include <vm/vm_extern.h> 49235537Sgber 50235537Sgber#include "nandfs_mount.h" 51235537Sgber#include "nandfs.h" 52235537Sgber#include "nandfs_subr.h" 53235537Sgber 54235537Sgberint 55235537Sgbernandfs_add_dirent(struct vnode *dvp, uint64_t ino, char *nameptr, long namelen, 56235537Sgber uint8_t type) 57235537Sgber{ 58235537Sgber struct nandfs_node *dir_node = VTON(dvp); 59235537Sgber struct nandfs_dir_entry *dirent, *pdirent; 60235537Sgber uint32_t blocksize = dir_node->nn_nandfsdev->nd_blocksize; 61235537Sgber uint64_t filesize = dir_node->nn_inode.i_size; 62235537Sgber uint64_t inode_blks = dir_node->nn_inode.i_blocks; 63235537Sgber uint32_t off, rest; 64235537Sgber uint8_t *pos; 65235537Sgber struct buf *bp; 66235537Sgber int error; 67235537Sgber 68235537Sgber pdirent = NULL; 69235537Sgber bp = NULL; 70235537Sgber if (inode_blks) { 71235537Sgber error = nandfs_bread(dir_node, inode_blks - 1, NOCRED, 0, &bp); 72235537Sgber if (error) { 73235537Sgber brelse(bp); 74235537Sgber return (error); 75235537Sgber } 76235537Sgber 77235537Sgber pos = bp->b_data; 78235537Sgber off = 0; 79235537Sgber while (off < blocksize) { 80235537Sgber pdirent = (struct nandfs_dir_entry *) (pos + off); 81235537Sgber if (!pdirent->rec_len) { 82235537Sgber pdirent = NULL; 83235537Sgber break; 84235537Sgber } 85235537Sgber off += pdirent->rec_len; 86235537Sgber } 87235537Sgber 88235537Sgber if (pdirent) 89235537Sgber rest = pdirent->rec_len - 90235537Sgber NANDFS_DIR_REC_LEN(pdirent->name_len); 91235537Sgber else 92235537Sgber rest = blocksize; 93235537Sgber 94235537Sgber if (rest < NANDFS_DIR_REC_LEN(namelen)) { 95235537Sgber /* Do not update pdirent as new block is created */ 96235537Sgber pdirent = NULL; 97235537Sgber brelse(bp); 98235537Sgber /* Set to NULL to create new */ 99235537Sgber bp = NULL; 100235537Sgber filesize += rest; 101235537Sgber } 102235537Sgber } 103235537Sgber 104235537Sgber /* If no bp found create new */ 105235537Sgber if (!bp) { 106235537Sgber error = nandfs_bcreate(dir_node, inode_blks, NOCRED, 0, &bp); 107235537Sgber if (error) 108235537Sgber return (error); 109235537Sgber off = 0; 110235537Sgber pos = bp->b_data; 111235537Sgber } 112235537Sgber 113235537Sgber /* Modify pdirent if exists */ 114235537Sgber if (pdirent) { 115235537Sgber DPRINTF(LOOKUP, ("modify pdirent %p\n", pdirent)); 116235537Sgber /* modify last de */ 117235537Sgber off -= pdirent->rec_len; 118235537Sgber pdirent->rec_len = 119235537Sgber NANDFS_DIR_REC_LEN(pdirent->name_len); 120235537Sgber off += pdirent->rec_len; 121235537Sgber } 122235537Sgber 123235537Sgber /* Create new dirent */ 124235537Sgber dirent = (struct nandfs_dir_entry *) (pos + off); 125235537Sgber dirent->rec_len = blocksize - off; 126235537Sgber dirent->inode = ino; 127235537Sgber dirent->name_len = namelen; 128235537Sgber memset(dirent->name, 0, NANDFS_DIR_NAME_LEN(namelen)); 129235537Sgber memcpy(dirent->name, nameptr, namelen); 130235537Sgber dirent->file_type = type; 131235537Sgber 132235537Sgber filesize += NANDFS_DIR_REC_LEN(dirent->name_len); 133235537Sgber 134235537Sgber DPRINTF(LOOKUP, ("create dir_entry '%.*s' at %p with size %x " 135235537Sgber "new filesize: %jx\n", 136235537Sgber (int)namelen, dirent->name, dirent, dirent->rec_len, 137235537Sgber (uintmax_t)filesize)); 138235537Sgber 139235537Sgber error = nandfs_dirty_buf(bp, 0); 140235537Sgber if (error) 141235537Sgber return (error); 142235537Sgber 143235537Sgber dir_node->nn_inode.i_size = filesize; 144235537Sgber dir_node->nn_flags |= IN_CHANGE | IN_UPDATE; 145235537Sgber vnode_pager_setsize(dvp, filesize); 146235537Sgber 147235537Sgber return (0); 148235537Sgber} 149235537Sgber 150235537Sgberint 151235537Sgbernandfs_remove_dirent(struct vnode *dvp, struct nandfs_node *node, 152235537Sgber struct componentname *cnp) 153235537Sgber{ 154235537Sgber struct nandfs_node *dir_node; 155235537Sgber struct nandfs_dir_entry *dirent, *pdirent; 156235537Sgber struct buf *bp; 157235537Sgber uint64_t filesize, blocknr, ino, offset; 158235537Sgber uint32_t blocksize, limit, off; 159235537Sgber uint16_t newsize; 160235537Sgber uint8_t *pos; 161235537Sgber int error, found; 162235537Sgber 163235537Sgber dir_node = VTON(dvp); 164235537Sgber filesize = dir_node->nn_inode.i_size; 165235537Sgber if (!filesize) 166235537Sgber return (0); 167235537Sgber 168235537Sgber if (node) { 169235537Sgber offset = node->nn_diroff; 170235537Sgber ino = node->nn_ino; 171235537Sgber } else { 172235537Sgber offset = dir_node->nn_diroff; 173235537Sgber ino = NANDFS_WHT_INO; 174235537Sgber } 175235537Sgber 176235537Sgber dirent = pdirent = NULL; 177235537Sgber blocksize = dir_node->nn_nandfsdev->nd_blocksize; 178235537Sgber blocknr = offset / blocksize; 179235537Sgber 180235537Sgber DPRINTF(LOOKUP, ("rm direntry dvp %p node %p ino %#jx at off %#jx\n", 181235537Sgber dvp, node, (uintmax_t)ino, (uintmax_t)offset)); 182235537Sgber 183235537Sgber error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp); 184235537Sgber if (error) { 185235537Sgber brelse(bp); 186235537Sgber return (error); 187235537Sgber } 188235537Sgber 189235537Sgber pos = bp->b_data; 190235537Sgber off = 0; 191235537Sgber found = 0; 192235537Sgber limit = offset % blocksize; 193235537Sgber pdirent = (struct nandfs_dir_entry *) bp->b_data; 194235537Sgber while (off <= limit) { 195235537Sgber dirent = (struct nandfs_dir_entry *) (pos + off); 196235537Sgber 197235537Sgber if ((off == limit) && 198235537Sgber (dirent->inode == ino)) { 199235537Sgber found = 1; 200235537Sgber break; 201235537Sgber } 202235537Sgber if (dirent->inode != 0) 203235537Sgber pdirent = dirent; 204235537Sgber off += dirent->rec_len; 205235537Sgber } 206235537Sgber 207235537Sgber if (!found) { 208235537Sgber nandfs_error("cannot find entry to remove"); 209235537Sgber brelse(bp); 210235537Sgber return (error); 211235537Sgber } 212235537Sgber DPRINTF(LOOKUP, 213235537Sgber ("rm dirent ino %#jx at %#x with size %#x\n", 214235537Sgber (uintmax_t)dirent->inode, off, dirent->rec_len)); 215235537Sgber 216235537Sgber newsize = (uintptr_t)dirent - (uintptr_t)pdirent; 217235537Sgber newsize += dirent->rec_len; 218235537Sgber pdirent->rec_len = newsize; 219235537Sgber dirent->inode = 0; 220235537Sgber error = nandfs_dirty_buf(bp, 0); 221235537Sgber if (error) 222235537Sgber return (error); 223235537Sgber 224235537Sgber dir_node->nn_flags |= IN_CHANGE | IN_UPDATE; 225235537Sgber /* If last one modify filesize */ 226235537Sgber if ((offset + NANDFS_DIR_REC_LEN(dirent->name_len)) == filesize) { 227235537Sgber filesize = blocknr * blocksize + 228235537Sgber ((uintptr_t)pdirent - (uintptr_t)pos) + 229235537Sgber NANDFS_DIR_REC_LEN(pdirent->name_len); 230235537Sgber dir_node->nn_inode.i_size = filesize; 231235537Sgber } 232235537Sgber 233235537Sgber return (0); 234235537Sgber} 235235537Sgber 236235537Sgberint 237235537Sgbernandfs_update_parent_dir(struct vnode *dvp, uint64_t newparent) 238235537Sgber{ 239235537Sgber struct nandfs_dir_entry *dirent; 240235537Sgber struct nandfs_node *dir_node; 241235537Sgber struct buf *bp; 242235537Sgber int error; 243235537Sgber 244235537Sgber dir_node = VTON(dvp); 245235537Sgber error = nandfs_bread(dir_node, 0, NOCRED, 0, &bp); 246235537Sgber if (error) { 247235537Sgber brelse(bp); 248235537Sgber return (error); 249235537Sgber } 250235537Sgber dirent = (struct nandfs_dir_entry *)bp->b_data; 251235537Sgber dirent->inode = newparent; 252235537Sgber error = nandfs_dirty_buf(bp, 0); 253235537Sgber if (error) 254235537Sgber return (error); 255235537Sgber 256235537Sgber return (0); 257235537Sgber} 258235537Sgber 259235537Sgberint 260235537Sgbernandfs_update_dirent(struct vnode *dvp, struct nandfs_node *fnode, 261235537Sgber struct nandfs_node *tnode) 262235537Sgber{ 263235537Sgber struct nandfs_node *dir_node; 264235537Sgber struct nandfs_dir_entry *dirent; 265235537Sgber struct buf *bp; 266235537Sgber uint64_t file_size, blocknr; 267235537Sgber uint32_t blocksize, off; 268235537Sgber uint8_t *pos; 269235537Sgber int error; 270235537Sgber 271235537Sgber dir_node = VTON(dvp); 272235537Sgber file_size = dir_node->nn_inode.i_size; 273235537Sgber if (!file_size) 274235537Sgber return (0); 275235537Sgber 276235537Sgber DPRINTF(LOOKUP, 277235537Sgber ("chg direntry dvp %p ino %#jx to in %#jx at off %#jx\n", 278235537Sgber dvp, (uintmax_t)tnode->nn_ino, (uintmax_t)fnode->nn_ino, 279235537Sgber (uintmax_t)tnode->nn_diroff)); 280235537Sgber 281235537Sgber blocksize = dir_node->nn_nandfsdev->nd_blocksize; 282235537Sgber blocknr = tnode->nn_diroff / blocksize; 283235537Sgber off = tnode->nn_diroff % blocksize; 284235537Sgber error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp); 285235537Sgber if (error) { 286235537Sgber brelse(bp); 287235537Sgber return (error); 288235537Sgber } 289235537Sgber 290235537Sgber pos = bp->b_data; 291235537Sgber dirent = (struct nandfs_dir_entry *) (pos + off); 292235537Sgber KASSERT((dirent->inode == tnode->nn_ino), 293235537Sgber ("direntry mismatch")); 294235537Sgber 295235537Sgber dirent->inode = fnode->nn_ino; 296235537Sgber error = nandfs_dirty_buf(bp, 0); 297235537Sgber if (error) 298235537Sgber return (error); 299235537Sgber 300235537Sgber return (0); 301235537Sgber} 302235537Sgber 303235537Sgberint 304235537Sgbernandfs_init_dir(struct vnode *dvp, uint64_t ino, uint64_t parent_ino) 305235537Sgber{ 306235537Sgber 307235537Sgber if (nandfs_add_dirent(dvp, parent_ino, "..", 2, DT_DIR) || 308235537Sgber nandfs_add_dirent(dvp, ino, ".", 1, DT_DIR)) { 309235537Sgber nandfs_error("%s: cannot initialize dir ino:%jd(pino:%jd)\n", 310235537Sgber __func__, ino, parent_ino); 311235537Sgber return (-1); 312235537Sgber } 313235537Sgber return (0); 314235537Sgber} 315