1/*- 2 * Copyright (c) 2010-2012 Semihalf 3 * Copyright (c) 2008, 2009 Reinoud Zandijk 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 * From: NetBSD: nilfs_subr.c,v 1.4 2009/07/29 17:06:57 reinoud 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD$"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/namei.h> 35#include <sys/kernel.h> 36#include <sys/stat.h> 37#include <sys/buf.h> 38#include <sys/bio.h> 39#include <sys/proc.h> 40#include <sys/mount.h> 41#include <sys/vnode.h> 42#include <sys/signalvar.h> 43#include <sys/malloc.h> 44#include <sys/dirent.h> 45#include <sys/lockf.h> 46 47#include <vm/vm.h> 48#include <vm/vm_extern.h> 49 50#include "nandfs_mount.h" 51#include "nandfs.h" 52#include "nandfs_subr.h" 53 54int 55nandfs_add_dirent(struct vnode *dvp, uint64_t ino, char *nameptr, long namelen, 56 uint8_t type) 57{ 58 struct nandfs_node *dir_node = VTON(dvp); 59 struct nandfs_dir_entry *dirent, *pdirent; 60 uint32_t blocksize = dir_node->nn_nandfsdev->nd_blocksize; 61 uint64_t filesize = dir_node->nn_inode.i_size; 62 uint64_t inode_blks = dir_node->nn_inode.i_blocks; 63 uint32_t off, rest; 64 uint8_t *pos; 65 struct buf *bp; 66 int error; 67 68 pdirent = NULL; 69 bp = NULL; 70 if (inode_blks) { 71 error = nandfs_bread(dir_node, inode_blks - 1, NOCRED, 0, &bp); 72 if (error) { 73 brelse(bp); 74 return (error); 75 } 76 77 pos = bp->b_data; 78 off = 0; 79 while (off < blocksize) { 80 pdirent = (struct nandfs_dir_entry *) (pos + off); 81 if (!pdirent->rec_len) { 82 pdirent = NULL; 83 break; 84 } 85 off += pdirent->rec_len; 86 } 87 88 if (pdirent) 89 rest = pdirent->rec_len - 90 NANDFS_DIR_REC_LEN(pdirent->name_len); 91 else 92 rest = blocksize; 93 94 if (rest < NANDFS_DIR_REC_LEN(namelen)) { 95 /* Do not update pdirent as new block is created */ 96 pdirent = NULL; 97 brelse(bp); 98 /* Set to NULL to create new */ 99 bp = NULL; 100 filesize += rest; 101 } 102 } 103 104 /* If no bp found create new */ 105 if (!bp) { 106 error = nandfs_bcreate(dir_node, inode_blks, NOCRED, 0, &bp); 107 if (error) 108 return (error); 109 off = 0; 110 pos = bp->b_data; 111 } 112 113 /* Modify pdirent if exists */ 114 if (pdirent) { 115 DPRINTF(LOOKUP, ("modify pdirent %p\n", pdirent)); 116 /* modify last de */ 117 off -= pdirent->rec_len; 118 pdirent->rec_len = 119 NANDFS_DIR_REC_LEN(pdirent->name_len); 120 off += pdirent->rec_len; 121 } 122 123 /* Create new dirent */ 124 dirent = (struct nandfs_dir_entry *) (pos + off); 125 dirent->rec_len = blocksize - off; 126 dirent->inode = ino; 127 dirent->name_len = namelen; 128 memset(dirent->name, 0, NANDFS_DIR_NAME_LEN(namelen)); 129 memcpy(dirent->name, nameptr, namelen); 130 dirent->file_type = type; 131 132 filesize += NANDFS_DIR_REC_LEN(dirent->name_len); 133 134 DPRINTF(LOOKUP, ("create dir_entry '%.*s' at %p with size %x " 135 "new filesize: %jx\n", 136 (int)namelen, dirent->name, dirent, dirent->rec_len, 137 (uintmax_t)filesize)); 138 139 error = nandfs_dirty_buf(bp, 0); 140 if (error) 141 return (error); 142 143 dir_node->nn_inode.i_size = filesize; 144 dir_node->nn_flags |= IN_CHANGE | IN_UPDATE; 145 vnode_pager_setsize(dvp, filesize); 146 147 return (0); 148} 149 150int 151nandfs_remove_dirent(struct vnode *dvp, struct nandfs_node *node, 152 struct componentname *cnp) 153{ 154 struct nandfs_node *dir_node; 155 struct nandfs_dir_entry *dirent, *pdirent; 156 struct buf *bp; 157 uint64_t filesize, blocknr, ino, offset; 158 uint32_t blocksize, limit, off; 159 uint16_t newsize; 160 uint8_t *pos; 161 int error, found; 162 163 dir_node = VTON(dvp); 164 filesize = dir_node->nn_inode.i_size; 165 if (!filesize) 166 return (0); 167 168 if (node) { 169 offset = node->nn_diroff; 170 ino = node->nn_ino; 171 } else { 172 offset = dir_node->nn_diroff; 173 ino = NANDFS_WHT_INO; 174 } 175 176 dirent = pdirent = NULL; 177 blocksize = dir_node->nn_nandfsdev->nd_blocksize; 178 blocknr = offset / blocksize; 179 180 DPRINTF(LOOKUP, ("rm direntry dvp %p node %p ino %#jx at off %#jx\n", 181 dvp, node, (uintmax_t)ino, (uintmax_t)offset)); 182 183 error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp); 184 if (error) { 185 brelse(bp); 186 return (error); 187 } 188 189 pos = bp->b_data; 190 off = 0; 191 found = 0; 192 limit = offset % blocksize; 193 pdirent = (struct nandfs_dir_entry *) bp->b_data; 194 while (off <= limit) { 195 dirent = (struct nandfs_dir_entry *) (pos + off); 196 197 if ((off == limit) && 198 (dirent->inode == ino)) { 199 found = 1; 200 break; 201 } 202 if (dirent->inode != 0) 203 pdirent = dirent; 204 off += dirent->rec_len; 205 } 206 207 if (!found) { 208 nandfs_error("cannot find entry to remove"); 209 brelse(bp); 210 return (error); 211 } 212 DPRINTF(LOOKUP, 213 ("rm dirent ino %#jx at %#x with size %#x\n", 214 (uintmax_t)dirent->inode, off, dirent->rec_len)); 215 216 newsize = (uintptr_t)dirent - (uintptr_t)pdirent; 217 newsize += dirent->rec_len; 218 pdirent->rec_len = newsize; 219 dirent->inode = 0; 220 error = nandfs_dirty_buf(bp, 0); 221 if (error) 222 return (error); 223 224 dir_node->nn_flags |= IN_CHANGE | IN_UPDATE; 225 /* If last one modify filesize */ 226 if ((offset + NANDFS_DIR_REC_LEN(dirent->name_len)) == filesize) { 227 filesize = blocknr * blocksize + 228 ((uintptr_t)pdirent - (uintptr_t)pos) + 229 NANDFS_DIR_REC_LEN(pdirent->name_len); 230 dir_node->nn_inode.i_size = filesize; 231 } 232 233 return (0); 234} 235 236int 237nandfs_update_parent_dir(struct vnode *dvp, uint64_t newparent) 238{ 239 struct nandfs_dir_entry *dirent; 240 struct nandfs_node *dir_node; 241 struct buf *bp; 242 int error; 243 244 dir_node = VTON(dvp); 245 error = nandfs_bread(dir_node, 0, NOCRED, 0, &bp); 246 if (error) { 247 brelse(bp); 248 return (error); 249 } 250 dirent = (struct nandfs_dir_entry *)bp->b_data; 251 dirent->inode = newparent; 252 error = nandfs_dirty_buf(bp, 0); 253 if (error) 254 return (error); 255 256 return (0); 257} 258 259int 260nandfs_update_dirent(struct vnode *dvp, struct nandfs_node *fnode, 261 struct nandfs_node *tnode) 262{ 263 struct nandfs_node *dir_node; 264 struct nandfs_dir_entry *dirent; 265 struct buf *bp; 266 uint64_t file_size, blocknr; 267 uint32_t blocksize, off; 268 uint8_t *pos; 269 int error; 270 271 dir_node = VTON(dvp); 272 file_size = dir_node->nn_inode.i_size; 273 if (!file_size) 274 return (0); 275 276 DPRINTF(LOOKUP, 277 ("chg direntry dvp %p ino %#jx to in %#jx at off %#jx\n", 278 dvp, (uintmax_t)tnode->nn_ino, (uintmax_t)fnode->nn_ino, 279 (uintmax_t)tnode->nn_diroff)); 280 281 blocksize = dir_node->nn_nandfsdev->nd_blocksize; 282 blocknr = tnode->nn_diroff / blocksize; 283 off = tnode->nn_diroff % blocksize; 284 error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp); 285 if (error) { 286 brelse(bp); 287 return (error); 288 } 289 290 pos = bp->b_data; 291 dirent = (struct nandfs_dir_entry *) (pos + off); 292 KASSERT((dirent->inode == tnode->nn_ino), 293 ("direntry mismatch")); 294 295 dirent->inode = fnode->nn_ino; 296 error = nandfs_dirty_buf(bp, 0); 297 if (error) 298 return (error); 299 300 return (0); 301} 302 303int 304nandfs_init_dir(struct vnode *dvp, uint64_t ino, uint64_t parent_ino) 305{ 306 307 if (nandfs_add_dirent(dvp, parent_ino, "..", 2, DT_DIR) || 308 nandfs_add_dirent(dvp, ino, ".", 1, DT_DIR)) { 309 nandfs_error("%s: cannot initialize dir ino:%jd(pino:%jd)\n", 310 __func__, ino, parent_ino); 311 return (-1); 312 } 313 return (0); 314} 315