1/* $NetBSD: v7fs_file.c,v 1.3 2011/07/30 03:51:53 uch Exp $ */ 2 3/*- 4 * Copyright (c) 2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by UCHIYAMA Yasushi. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#if HAVE_NBTOOL_CONFIG_H 33#include "nbtool_config.h" 34#endif 35 36#include <sys/cdefs.h> 37__KERNEL_RCSID(0, "$NetBSD: v7fs_file.c,v 1.3 2011/07/30 03:51:53 uch Exp $"); 38#if defined _KERNEL_OPT 39#include "opt_v7fs.h" 40#endif 41 42#ifdef _KERNEL 43#include <sys/systm.h> 44#include <sys/param.h> 45#else 46#include <stdio.h> 47#include <string.h> 48#include <errno.h> 49#define MIN(a,b) ((/*CONSTCOND*/(a)<(b))?(a):(b)) 50#endif 51 52#include "v7fs.h" 53#include "v7fs_impl.h" 54#include "v7fs_endian.h" 55#include "v7fs_inode.h" 56#include "v7fs_dirent.h" 57#include "v7fs_file.h" 58#include "v7fs_datablock.h" 59 60#ifdef V7FS_FILE_DEBUG 61#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) 62#else 63#define DPRINTF(fmt, args...) ((void)0) 64#endif 65 66static int lookup_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); 67static int remove_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); 68 69int 70v7fs_file_lookup_by_name(struct v7fs_self *fs, struct v7fs_inode *parent_dir, 71 const char *name, v7fs_ino_t *ino) 72{ 73 char filename[V7FS_NAME_MAX + 1]; 74 char *q; 75 int error; 76 size_t len; 77 78 if ((q = strchr(name, '/'))) { 79 /* Zap following path. */ 80 len = MIN(V7FS_NAME_MAX, q - name); 81 memcpy(filename, name, len); 82 filename[len] = '\0'; /* '/' -> '\0' */ 83 } else { 84 v7fs_dirent_filename(filename, name); 85 } 86 DPRINTF("%s(%s) dir=%d\n", filename, name, parent_dir->inode_number); 87 88 struct v7fs_lookup_arg lookup_arg = { .name = filename, 89 .inode_number = 0 }; 90 if ((error = v7fs_datablock_foreach(fs, parent_dir, lookup_subr, 91 &lookup_arg)) != V7FS_ITERATOR_BREAK) { 92 DPRINTF("not found.\n"); 93 return ENOENT; 94 } 95 96 *ino = lookup_arg.inode_number; 97 DPRINTF("done. ino=%d\n", *ino); 98 99 return 0; 100} 101 102static int 103lookup_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) 104{ 105 struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; 106 struct v7fs_dirent *dir; 107 const char *name = p->name; 108 void *buf; 109 size_t i, n; 110 int ret = 0; 111 112 if (!(buf = scratch_read(fs, blk))) 113 return EIO; 114 115 dir = (struct v7fs_dirent *)buf; 116 n = sz / sizeof(*dir); 117 v7fs_dirent_endian_convert(fs, dir, n); 118 119 for (i = 0; i < n; i++, dir++) { 120 if (dir->inode_number < 1) { 121 DPRINTF("*** bad inode #%d ***\n", dir->inode_number); 122 continue; 123 } 124 125 if (strncmp((const char *)dir->name, name, V7FS_NAME_MAX) == 0) 126 { 127 p->inode_number = dir->inode_number; 128 ret = V7FS_ITERATOR_BREAK; /* found */ 129 break; 130 } 131 } 132 scratch_free(fs, buf); 133 134 return ret; 135} 136 137int 138v7fs_file_allocate(struct v7fs_self *fs, struct v7fs_inode *parent_dir, 139 const char *srcname, struct v7fs_fileattr *attr, v7fs_ino_t *ino) 140{ 141 struct v7fs_inode inode; 142 char filename[V7FS_NAME_MAX + 1]; 143 struct v7fs_dirent *dir; 144 int error; 145 146 /* Truncate filename. */ 147 v7fs_dirent_filename(filename, srcname); 148 DPRINTF("%s(%s)\n", filename, srcname); 149 150 /* Check filename. */ 151 if (v7fs_file_lookup_by_name(fs, parent_dir, filename, ino) == 0) { 152 DPRINTF("%s exists\n", filename); 153 return EEXIST; 154 } 155 156 /* Get new inode. */ 157 if ((error = v7fs_inode_allocate(fs, ino))) 158 return error; 159 160 /* Set initial attribute. */ 161 memset(&inode, 0, sizeof(inode)); 162 inode.inode_number = *ino; 163 inode.mode = attr->mode; 164 inode.uid = attr->uid; 165 inode.gid = attr->gid; 166 if (attr->ctime) 167 inode.ctime = attr->ctime; 168 if (attr->mtime) 169 inode.mtime = attr->mtime; 170 if (attr->atime) 171 inode.atime = attr->atime; 172 173 switch (inode.mode & V7FS_IFMT) { 174 default: 175 DPRINTF("Can't allocate %o type.\n", inode.mode); 176 v7fs_inode_deallocate(fs, *ino); 177 return EINVAL; 178 case V7FS_IFCHR: 179 /* FALLTHROUGH */ 180 case V7FS_IFBLK: 181 inode.nlink = 1; 182 inode.device = attr->device; 183 inode.addr[0] = inode.device; 184 break; 185 case V7FSBSD_IFFIFO: 186 /* FALLTHROUGH */ 187 case V7FSBSD_IFSOCK: 188 /* FALLTHROUGH */ 189 case V7FSBSD_IFLNK: 190 /* FALLTHROUGH */ 191 case V7FS_IFREG: 192 inode.nlink = 1; 193 break; 194 case V7FS_IFDIR: 195 inode.nlink = 2; /* . + .. */ 196 if ((error = v7fs_datablock_expand(fs, &inode, sizeof(*dir) * 2 197 ))) { 198 v7fs_inode_deallocate(fs, *ino); 199 return error; 200 } 201 v7fs_daddr_t blk = inode.addr[0]; 202 void *buf; 203 if (!(buf = scratch_read(fs, blk))) { 204 v7fs_inode_deallocate(fs, *ino); 205 return EIO; 206 } 207 dir = (struct v7fs_dirent *)buf; 208 strcpy(dir[0].name, "."); 209 dir[0].inode_number = V7FS_VAL16(fs, *ino); 210 strcpy(dir[1].name, ".."); 211 dir[1].inode_number = V7FS_VAL16(fs, parent_dir->inode_number); 212 if (!fs->io.write(fs->io.cookie, buf, blk)) { 213 scratch_free(fs, buf); 214 return EIO; 215 } 216 scratch_free(fs, buf); 217 break; 218 } 219 220 v7fs_inode_writeback(fs, &inode); 221 222 /* Link this inode to parent directory. */ 223 if ((error = v7fs_directory_add_entry(fs, parent_dir, *ino, filename))) 224 { 225 DPRINTF("can't add dirent.\n"); 226 return error; 227 } 228 229 return 0; 230} 231 232int 233v7fs_file_deallocate(struct v7fs_self *fs, struct v7fs_inode *parent_dir, 234 const char *name) 235{ 236 v7fs_ino_t ino; 237 struct v7fs_inode inode; 238 int error; 239 240 DPRINTF("%s\n", name); 241 if ((error = v7fs_file_lookup_by_name(fs, parent_dir, name, &ino))) { 242 DPRINTF("no such a file: %s\n", name); 243 return error; 244 } 245 DPRINTF("%s->#%d\n", name, ino); 246 if ((error = v7fs_inode_load(fs, &inode, ino))) 247 return error; 248 249 if (v7fs_inode_isdir(&inode)) { 250 char filename[V7FS_NAME_MAX + 1]; 251 v7fs_dirent_filename(filename, name); 252 /* Check parent */ 253 if (strncmp(filename, "..", V7FS_NAME_MAX) == 0) { 254 DPRINTF("can not remove '..'\n"); 255 return EINVAL; /* t_vnops rename_dotdot */ 256 } 257 /* Check empty */ 258 if (v7fs_inode_filesize(&inode) != 259 sizeof(struct v7fs_dirent) * 2 /*"." + ".."*/) { 260 DPRINTF("directory not empty.\n"); 261 return ENOTEMPTY;/* t_vnops dir_noempty, rename_dir(6)*/ 262 } 263 inode.nlink = 0; /* remove this. */ 264 } else { 265 /* Decrement reference count. */ 266 --inode.nlink; /* regular file. */ 267 DPRINTF("%s nlink=%d\n", name, inode.nlink); 268 } 269 270 271 if ((error = v7fs_directory_remove_entry(fs, parent_dir, name))) 272 return error; 273 DPRINTF("remove dirent\n"); 274 275 if (inode.nlink == 0) { 276 v7fs_datablock_contract(fs, &inode, inode.filesize); 277 DPRINTF("remove datablock\n"); 278 v7fs_inode_deallocate(fs, ino); 279 DPRINTF("remove inode\n"); 280 } else { 281 v7fs_inode_writeback(fs, &inode); 282 } 283 284 return 0; 285} 286 287int 288v7fs_directory_add_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir, 289 v7fs_ino_t ino, const char *srcname) 290{ 291 struct v7fs_inode inode; 292 struct v7fs_dirent *dir; 293 int error = 0; 294 v7fs_daddr_t blk; 295 void *buf; 296 char filename[V7FS_NAME_MAX + 1]; 297 298 /* Truncate filename. */ 299 v7fs_dirent_filename(filename, srcname); 300 DPRINTF("%s(%s) %d\n", filename, srcname, ino); 301 302 /* Target inode */ 303 if ((error = v7fs_inode_load(fs, &inode, ino))) 304 return error; 305 306 /* Expand datablock. */ 307 if ((error = v7fs_datablock_expand(fs, parent_dir, sizeof(*dir)))) 308 return error; 309 310 /* Read last entry. */ 311 if (!(blk = v7fs_datablock_last(fs, parent_dir, 312 v7fs_inode_filesize(parent_dir)))) 313 return EIO; 314 315 /* Load dirent block. This vnode(parent dir) is locked by VFS layer. */ 316 if (!(buf = scratch_read(fs, blk))) 317 return EIO; 318 319 size_t sz = v7fs_inode_filesize(parent_dir); 320 sz = V7FS_RESIDUE_BSIZE(sz); /* last block payload. */ 321 int n = sz / sizeof(*dir) - 1; 322 /* Add dirent. */ 323 dir = (struct v7fs_dirent *)buf; 324 dir[n].inode_number = V7FS_VAL16(fs, ino); 325 memcpy((char *)dir[n].name, filename, V7FS_NAME_MAX); 326 /* Write back datablock */ 327 if (!fs->io.write(fs->io.cookie, buf, blk)) 328 error = EIO; 329 scratch_free(fs, buf); 330 331 if (v7fs_inode_isdir(&inode)) { 332 parent_dir->nlink++; 333 v7fs_inode_writeback(fs, parent_dir); 334 } 335 336 DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize); 337 338 return error; 339} 340 341int 342v7fs_directory_remove_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir, 343 const char *name) 344{ 345 struct v7fs_inode inode; 346 int error; 347 struct v7fs_dirent lastdirent; 348 v7fs_daddr_t lastblk; 349 size_t sz, lastsz; 350 v7fs_off_t pos; 351 void *buf; 352 353 /* Setup replaced entry. */ 354 sz = parent_dir->filesize; 355 lastblk = v7fs_datablock_last(fs, parent_dir, 356 v7fs_inode_filesize(parent_dir)); 357 lastsz = V7FS_RESIDUE_BSIZE(sz); 358 pos = lastsz - sizeof(lastdirent); 359 360 if (!(buf = scratch_read(fs, lastblk))) 361 return EIO; 362 lastdirent = *((struct v7fs_dirent *)((uint8_t *)buf + pos)); 363 scratch_free(fs, buf); 364 DPRINTF("last dirent=%d %s pos=%d\n", 365 V7FS_VAL16(fs, lastdirent.inode_number), lastdirent.name, pos); 366 367 struct v7fs_lookup_arg lookup_arg = 368 { .name = name, .replace = &lastdirent/*disk endian */ }; 369 /* Search entry that removed. replace it to last dirent. */ 370 if ((error = v7fs_datablock_foreach(fs, parent_dir, remove_subr, 371 &lookup_arg)) != V7FS_ITERATOR_BREAK) 372 return ENOENT; 373 374 /* Contract dirent entries. */ 375 v7fs_datablock_contract(fs, parent_dir, sizeof(lastdirent)); 376 DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize); 377 378 /* Target inode */ 379 if ((error = v7fs_inode_load(fs, &inode, lookup_arg.inode_number))) 380 return error; 381 382 if (v7fs_inode_isdir(&inode)) { 383 parent_dir->nlink--; 384 v7fs_inode_writeback(fs, parent_dir); 385 } 386 387 return 0; 388} 389 390static int 391remove_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) 392{ 393 struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; 394 struct v7fs_dirent *dir; 395 void *buf; 396 size_t i; 397 int ret = 0; 398 399 DPRINTF("match start blk=%x\n", blk); 400 if (!(buf = scratch_read(fs, blk))) 401 return EIO; 402 403 dir = (struct v7fs_dirent *)buf; 404 405 for (i = 0; i < sz / sizeof(*dir); i++, dir++) { 406 DPRINTF("%d\n", V7FS_VAL16(fs, dir->inode_number)); 407 if (strncmp(p->name, 408 (const char *)dir->name, V7FS_NAME_MAX) == 0) { 409 p->inode_number = V7FS_VAL16(fs, dir->inode_number); 410 /* Replace to last dirent. */ 411 *dir = *(p->replace); /* disk endian */ 412 /* Write back. */ 413 if (!fs->io.write(fs->io.cookie, buf, blk)) 414 ret = EIO; 415 else 416 ret = V7FS_ITERATOR_BREAK; 417 break; 418 } 419 } 420 scratch_free(fs, buf); 421 422 return ret; 423} 424