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