1/* $NetBSD: v7fs_file.c,v 1.7 2022/02/11 10:55:15 hannken 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.7 2022/02/11 10:55:15 hannken Exp $"); 38#if defined _KERNEL_OPT 39#include "opt_v7fs.h" 40#endif 41 42#include <sys/param.h> 43#ifdef _KERNEL 44#include <sys/systm.h> 45#else 46#include <stdio.h> 47#include <string.h> 48#include <errno.h> 49#endif 50 51#include "v7fs.h" 52#include "v7fs_impl.h" 53#include "v7fs_endian.h" 54#include "v7fs_inode.h" 55#include "v7fs_dirent.h" 56#include "v7fs_file.h" 57#include "v7fs_datablock.h" 58 59#ifdef V7FS_FILE_DEBUG 60#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) 61#else 62#define DPRINTF(fmt, args...) ((void)0) 63#endif 64 65static int lookup_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); 66static int remove_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); 67 68int 69v7fs_file_lookup_by_name(struct v7fs_self *fs, struct v7fs_inode *parent_dir, 70 const char *name, size_t namelen, v7fs_ino_t *ino) 71{ 72 char filename[V7FS_NAME_MAX + 1]; 73 int error; 74 75 v7fs_dirent_filename(filename, name, namelen); 76 DPRINTF("%s(%.*s) dir=%d\n", filename, (int)namelen, name, 77 parent_dir->inode_number); 78 79 struct v7fs_lookup_arg lookup_arg = { .name = filename, 80 .inode_number = 0 }; 81 if ((error = v7fs_datablock_foreach(fs, parent_dir, lookup_subr, 82 &lookup_arg)) != V7FS_ITERATOR_BREAK) { 83 DPRINTF("not found.\n"); 84 return ENOENT; 85 } 86 87 *ino = lookup_arg.inode_number; 88 DPRINTF("done. ino=%d\n", *ino); 89 90 return 0; 91} 92 93static int 94lookup_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) 95{ 96 struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; 97 struct v7fs_dirent *dir; 98 const char *name = p->name; 99 void *buf; 100 size_t i, n; 101 int ret = 0; 102 103 if (!(buf = scratch_read(fs, blk))) 104 return EIO; 105 106 dir = (struct v7fs_dirent *)buf; 107 n = sz / sizeof(*dir); 108 v7fs_dirent_endian_convert(fs, dir, n); 109 110 for (i = 0; i < n; i++, dir++) { 111 if (dir->inode_number < 1) { 112 DPRINTF("*** bad inode #%d ***\n", dir->inode_number); 113 continue; 114 } 115 116 if (strncmp((const char *)dir->name, name, V7FS_NAME_MAX) == 0) 117 { 118 p->inode_number = dir->inode_number; 119 ret = V7FS_ITERATOR_BREAK; /* found */ 120 break; 121 } 122 } 123 scratch_free(fs, buf); 124 125 return ret; 126} 127 128int 129v7fs_file_allocate(struct v7fs_self *fs, struct v7fs_inode *parent_dir, 130 const char *srcname, size_t srclen, struct v7fs_fileattr *attr, 131 v7fs_ino_t *ino) 132{ 133 struct v7fs_inode inode; 134 struct v7fs_dirent *dir; 135 int error; 136 137 /* Check filename. */ 138 if (v7fs_file_lookup_by_name(fs, parent_dir, srcname, srclen, 139 ino) == 0) { 140 DPRINTF("%.*s exists\n", (int)srclen, srcname); 141 return EEXIST; 142 } 143 144 /* Get new inode. */ 145 if ((error = v7fs_inode_allocate(fs, ino))) 146 return error; 147 148 /* Set initial attribute. */ 149 memset(&inode, 0, sizeof(inode)); 150 inode.inode_number = *ino; 151 inode.mode = attr->mode; 152 inode.uid = attr->uid; 153 inode.gid = attr->gid; 154 if (attr->ctime) 155 inode.ctime = attr->ctime; 156 if (attr->mtime) 157 inode.mtime = attr->mtime; 158 if (attr->atime) 159 inode.atime = attr->atime; 160 161 switch (inode.mode & V7FS_IFMT) { 162 default: 163 DPRINTF("Can't allocate %o type.\n", inode.mode); 164 v7fs_inode_deallocate(fs, *ino); 165 return EINVAL; 166 case V7FS_IFCHR: 167 /* FALLTHROUGH */ 168 case V7FS_IFBLK: 169 inode.nlink = 1; 170 inode.device = attr->device; 171 inode.addr[0] = inode.device; 172 break; 173 case V7FSBSD_IFFIFO: 174 /* FALLTHROUGH */ 175 case V7FSBSD_IFSOCK: 176 /* FALLTHROUGH */ 177 case V7FSBSD_IFLNK: 178 /* FALLTHROUGH */ 179 case V7FS_IFREG: 180 inode.nlink = 1; 181 break; 182 case V7FS_IFDIR: 183 inode.nlink = 2; /* . + .. */ 184 if ((error = v7fs_datablock_expand(fs, &inode, sizeof(*dir) * 2 185 ))) { 186 v7fs_inode_deallocate(fs, *ino); 187 return error; 188 } 189 v7fs_daddr_t blk = inode.addr[0]; 190 void *buf; 191 if (!(buf = scratch_read(fs, blk))) { 192 v7fs_inode_deallocate(fs, *ino); 193 return EIO; 194 } 195 dir = (struct v7fs_dirent *)buf; 196 strcpy(dir[0].name, "."); 197 dir[0].inode_number = V7FS_VAL16(fs, *ino); 198 strcpy(dir[1].name, ".."); 199 dir[1].inode_number = V7FS_VAL16(fs, parent_dir->inode_number); 200 if (!fs->io.write(fs->io.cookie, buf, blk)) { 201 scratch_free(fs, buf); 202 return EIO; 203 } 204 scratch_free(fs, buf); 205 break; 206 } 207 208 v7fs_inode_writeback(fs, &inode); 209 210 /* Link this inode to parent directory. */ 211 if ((error = v7fs_directory_add_entry(fs, parent_dir, *ino, srcname, 212 srclen))) 213 { 214 DPRINTF("can't add dirent.\n"); 215 return error; 216 } 217 218 return 0; 219} 220 221int 222v7fs_file_deallocate(struct v7fs_self *fs, struct v7fs_inode *parent_dir, 223 const char *name, size_t namelen) 224{ 225 v7fs_ino_t ino; 226 struct v7fs_inode inode; 227 int error; 228 229 DPRINTF("%.*s\n", (int)namelen, name); 230 if ((error = v7fs_file_lookup_by_name(fs, parent_dir, name, namelen, 231 &ino))) { 232 DPRINTF("no such a file: %s\n", name); 233 return error; 234 } 235 DPRINTF("%.*s->#%d\n", (int)namelen, name, ino); 236 if ((error = v7fs_inode_load(fs, &inode, ino))) 237 return error; 238 239 if (v7fs_inode_isdir(&inode)) { 240 char filename[V7FS_NAME_MAX + 1]; 241 v7fs_dirent_filename(filename, name, namelen); 242 /* Check parent */ 243 if (strncmp(filename, "..", V7FS_NAME_MAX) == 0) { 244 DPRINTF("can not remove '..'\n"); 245 return EINVAL; /* t_vnops rename_dotdot */ 246 } 247 /* Check empty */ 248 if (v7fs_inode_filesize(&inode) != 249 sizeof(struct v7fs_dirent) * 2 /*"." + ".."*/) { 250 DPRINTF("directory not empty.\n"); 251 return ENOTEMPTY;/* t_vnops dir_noempty, rename_dir(6)*/ 252 } 253 error = v7fs_datablock_size_change(fs, 0, &inode); 254 if (error) 255 return error; 256 inode.nlink = 0; /* remove this. */ 257 } else { 258 /* Decrement reference count. */ 259 --inode.nlink; /* regular file. */ 260 DPRINTF("%.*s nlink=%d\n", (int)namelen, name, inode.nlink); 261 } 262 263 264 if ((error = v7fs_directory_remove_entry(fs, parent_dir, name, 265 namelen))) 266 return error; 267 DPRINTF("remove dirent\n"); 268 269 v7fs_inode_writeback(fs, &inode); 270 271 return 0; 272} 273 274int 275v7fs_directory_add_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir, 276 v7fs_ino_t ino, const char *srcname, size_t srclen) 277{ 278 struct v7fs_inode inode; 279 struct v7fs_dirent *dir; 280 int error = 0; 281 v7fs_daddr_t blk; 282 void *buf; 283 char filename[V7FS_NAME_MAX + 1]; 284 285 /* Truncate filename. */ 286 v7fs_dirent_filename(filename, srcname, srclen); 287 DPRINTF("%s(%.*s) %d\n", filename, (int)srclen, srcname, ino); 288 289 /* Target inode */ 290 if ((error = v7fs_inode_load(fs, &inode, ino))) 291 return error; 292 293 /* Expand datablock. */ 294 if ((error = v7fs_datablock_expand(fs, parent_dir, sizeof(*dir)))) 295 return error; 296 297 /* Read last entry. */ 298 if (!(blk = v7fs_datablock_last(fs, parent_dir, 299 v7fs_inode_filesize(parent_dir)))) 300 return EIO; 301 302 /* Load dirent block. This vnode(parent dir) is locked by VFS layer. */ 303 if (!(buf = scratch_read(fs, blk))) 304 return EIO; 305 306 size_t sz = v7fs_inode_filesize(parent_dir); 307 sz = V7FS_RESIDUE_BSIZE(sz); /* last block payload. */ 308 int n = sz / sizeof(*dir) - 1; 309 /* Add dirent. */ 310 dir = (struct v7fs_dirent *)buf; 311 dir[n].inode_number = V7FS_VAL16(fs, ino); 312 memcpy((char *)dir[n].name, filename, V7FS_NAME_MAX); 313 /* Write back datablock */ 314 if (!fs->io.write(fs->io.cookie, buf, blk)) 315 error = EIO; 316 scratch_free(fs, buf); 317 318 if (v7fs_inode_isdir(&inode)) { 319 parent_dir->nlink++; 320 v7fs_inode_writeback(fs, parent_dir); 321 } 322 323 DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize); 324 325 return error; 326} 327 328int 329v7fs_directory_remove_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir, 330 const char *name, size_t namelen) 331{ 332 struct v7fs_inode inode; 333 char filename[V7FS_NAME_MAX + 1]; 334 int error; 335 struct v7fs_dirent lastdirent; 336 v7fs_daddr_t lastblk; 337 size_t sz, lastsz; 338 v7fs_off_t pos; 339 void *buf; 340 341 v7fs_dirent_filename(filename, name, namelen); 342 343 /* Setup replaced entry. */ 344 sz = parent_dir->filesize; 345 lastblk = v7fs_datablock_last(fs, parent_dir, 346 v7fs_inode_filesize(parent_dir)); 347 lastsz = V7FS_RESIDUE_BSIZE(sz); 348 pos = lastsz - sizeof(lastdirent); 349 350 if (!(buf = scratch_read(fs, lastblk))) 351 return EIO; 352 lastdirent = *((struct v7fs_dirent *)((uint8_t *)buf + pos)); 353 scratch_free(fs, buf); 354 DPRINTF("last dirent=%d %s pos=%d\n", 355 V7FS_VAL16(fs, lastdirent.inode_number), lastdirent.name, pos); 356 357 struct v7fs_lookup_arg lookup_arg = 358 { .name = filename, .replace = &lastdirent/*disk endian */ }; 359 /* Search entry that removed. replace it to last dirent. */ 360 if ((error = v7fs_datablock_foreach(fs, parent_dir, remove_subr, 361 &lookup_arg)) != V7FS_ITERATOR_BREAK) 362 return ENOENT; 363 364 /* Contract dirent entries. */ 365 v7fs_datablock_contract(fs, parent_dir, sizeof(lastdirent)); 366 DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize); 367 368 /* Target inode */ 369 if ((error = v7fs_inode_load(fs, &inode, lookup_arg.inode_number))) 370 return error; 371 372 if (v7fs_inode_isdir(&inode)) { 373 parent_dir->nlink--; 374 v7fs_inode_writeback(fs, parent_dir); 375 } 376 377 return 0; 378} 379 380static int 381remove_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) 382{ 383 struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; 384 struct v7fs_dirent *dir; 385 void *buf; 386 size_t i; 387 int ret = 0; 388 389 DPRINTF("match start blk=%x\n", blk); 390 if (!(buf = scratch_read(fs, blk))) 391 return EIO; 392 393 dir = (struct v7fs_dirent *)buf; 394 395 for (i = 0; i < sz / sizeof(*dir); i++, dir++) { 396 DPRINTF("%d\n", V7FS_VAL16(fs, dir->inode_number)); 397 if (strncmp(p->name, 398 (const char *)dir->name, V7FS_NAME_MAX) == 0) { 399 p->inode_number = V7FS_VAL16(fs, dir->inode_number); 400 /* Replace to last dirent. */ 401 *dir = *(p->replace); /* disk endian */ 402 /* Write back. */ 403 if (!fs->io.write(fs->io.cookie, buf, blk)) 404 ret = EIO; 405 else 406 ret = V7FS_ITERATOR_BREAK; 407 break; 408 } 409 } 410 scratch_free(fs, buf); 411 412 return ret; 413} 414