1/* $NetBSD: v7fs_file_util.c,v 1.5 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_util.c,v 1.5 2022/02/11 10:55:15 hannken Exp $"); 38#ifdef _KERNEL 39#include <sys/systm.h> 40#include <sys/param.h> 41#else 42#include <stdio.h> 43#include <string.h> 44#include <errno.h> 45#endif 46 47#include "v7fs.h" 48#include "v7fs_impl.h" 49#include "v7fs_endian.h" 50#include "v7fs_inode.h" 51#include "v7fs_dirent.h" 52#include "v7fs_file.h" 53#include "v7fs_datablock.h" 54 55#ifdef V7FS_FILE_DEBUG 56#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) 57#else 58#define DPRINTF(fmt, args...) ((void)0) 59#endif 60 61static int replace_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); 62static int lookup_by_number_subr(struct v7fs_self *, void *, v7fs_daddr_t, 63 size_t); 64static int can_dirmove(struct v7fs_self *, v7fs_ino_t, v7fs_ino_t); 65static int lookup_parent_from_dir_subr(struct v7fs_self *, void *, 66 v7fs_daddr_t, size_t); 67 68int 69v7fs_file_link(struct v7fs_self *fs, struct v7fs_inode *parent_dir, 70 struct v7fs_inode *p, const char *name, size_t namelen) 71{ 72 int error = 0; 73 74 DPRINTF("%d %d %.*s\n", parent_dir->inode_number, p->inode_number, 75 (int)namelen, name); 76 if ((error = v7fs_directory_add_entry(fs, parent_dir, p->inode_number, 77 name, namelen))) { 78 DPRINTF("can't add entry"); 79 return error; 80 } 81 p->nlink++; 82 v7fs_inode_writeback(fs, p); 83 84 return 0; 85} 86 87int 88v7fs_file_symlink(struct v7fs_self *fs, struct v7fs_inode *p, 89 const char *target) 90{ 91 int error; 92 size_t len = strlen(target) + 1; 93 94 if (len > V7FSBSD_MAXSYMLINKLEN) {/* limited target 512byte pathname */ 95 DPRINTF("too long pathname."); 96 return ENAMETOOLONG; 97 } 98 99 if ((error = v7fs_datablock_expand(fs, p, len))) { 100 return error; 101 } 102 103 v7fs_daddr_t blk = p->addr[0]; /* 1block only. */ 104 void *buf; 105 if (!(buf = scratch_read(fs, blk))) { 106 return EIO; 107 } 108 109 strncpy(buf, target, V7FS_BSIZE); 110 if (!fs->io.write(fs->io.cookie, buf, blk)) { 111 scratch_free(fs, buf); 112 return EIO; 113 } 114 scratch_free(fs, buf); 115 v7fs_inode_writeback(fs, p); 116 117 return 0; 118} 119 120int 121v7fs_file_rename(struct v7fs_self *fs, struct v7fs_inode *parent_from, 122 const char *from, size_t fromlen, struct v7fs_inode *parent_to, 123 const char *to, size_t tolen) 124{ 125 v7fs_ino_t from_ino, to_ino; 126 struct v7fs_inode inode; 127 int error; 128 bool dir_move; 129 130 /* Check source file */ 131 if ((error = v7fs_file_lookup_by_name(fs, parent_from, from, fromlen, 132 &from_ino))) { 133 DPRINTF("%.*s don't exists\n", (int)fromlen, from); 134 return error; 135 } 136 v7fs_inode_load(fs, &inode, from_ino); 137 dir_move = v7fs_inode_isdir(&inode); 138 139 /* Check target file */ 140 error = v7fs_file_lookup_by_name(fs, parent_to, to, tolen, &to_ino); 141 if (error == 0) { /* found */ 142 DPRINTF("%.*s already exists\n", (int)tolen, to); 143 if ((error = v7fs_file_deallocate(fs, parent_to, to, tolen))) { 144 DPRINTF("%.*s can't remove %d\n", (int)tolen, 145 to, error); 146 return error; 147 } 148 } else if (error != ENOENT) { 149 DPRINTF("error=%d\n", error); 150 return error; 151 } 152 /* Check directory hierarchy. t_vnops rename_dir(5) */ 153 if (dir_move && (error = can_dirmove(fs, from_ino, 154 parent_to->inode_number))) { 155 DPRINTF("dst '%.*s' is child dir of '%.*s'. error=%d\n", 156 (int)tolen, to, (int)fromlen, from, error); 157 return error; 158 } 159 160 if ((error = v7fs_directory_add_entry(fs, parent_to, from_ino, to, 161 tolen))) { 162 DPRINTF("can't add entry"); 163 return error; 164 } 165 166 if ((error = v7fs_directory_remove_entry(fs, parent_from, from, 167 fromlen))) { 168 DPRINTF("can't remove entry"); 169 return error; 170 } 171 172 if (dir_move && (parent_from != parent_to)) { 173 /* If directory move, update ".." */ 174 if ((error = v7fs_directory_replace_entry(fs, &inode, "..", 175 parent_to->inode_number))) { 176 DPRINTF("can't replace parent dir"); 177 return error; 178 } 179 v7fs_inode_writeback(fs, &inode); 180 } 181 182 return 0; 183} 184 185 186int 187v7fs_directory_replace_entry(struct v7fs_self *fs, struct v7fs_inode *self_dir, 188 const char *name, v7fs_ino_t ino) 189{ 190 int error; 191 192 /* Search entry that replaced. replace it to new inode number. */ 193 struct v7fs_lookup_arg lookup_arg = { .name = name, 194 .inode_number = ino }; 195 if ((error = v7fs_datablock_foreach(fs, self_dir, replace_subr, 196 &lookup_arg)) != V7FS_ITERATOR_BREAK) 197 return ENOENT; 198 199 return 0; 200} 201 202static int 203replace_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) 204{ 205 struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; 206 struct v7fs_dirent *dir; 207 void *buf; 208 size_t i, n; 209 int ret = 0; 210 211 DPRINTF("match start blk=%x\n", blk); 212 if (!(buf = scratch_read(fs, blk))) 213 return EIO; 214 215 dir = (struct v7fs_dirent *)buf; 216 n = sz / sizeof(*dir); 217 218 for (i = 0; i < n; i++, dir++) { /*disk endian */ 219 if (strncmp(p->name, (const char *)dir->name, V7FS_NAME_MAX) 220 == 0) { 221 /* Replace inode# */ 222 dir->inode_number = V7FS_VAL16(fs, p->inode_number); 223 /* Write back. */ 224 if (!fs->io.write(fs->io.cookie, buf, blk)) 225 ret = EIO; 226 else 227 ret = V7FS_ITERATOR_BREAK; 228 break; 229 } 230 } 231 scratch_free(fs, buf); 232 233 return ret; 234} 235 236bool 237v7fs_file_lookup_by_number(struct v7fs_self *fs, struct v7fs_inode *parent_dir, 238 v7fs_ino_t ino, char *buf) 239{ 240 int ret; 241 242 ret = v7fs_datablock_foreach(fs, parent_dir, lookup_by_number_subr, 243 &(struct v7fs_lookup_arg){ .inode_number = ino, .buf = buf }); 244 245 return ret == V7FS_ITERATOR_BREAK; 246} 247 248static int 249lookup_by_number_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, 250 size_t sz) 251{ 252 struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; 253 struct v7fs_dirent *dir; 254 void *buf; 255 size_t i, n; 256 int ret = 0; 257 258 if (!(buf = scratch_read(fs, blk))) 259 return EIO; 260 261 dir = (struct v7fs_dirent *)buf; 262 n = sz / sizeof(*dir); 263 v7fs_dirent_endian_convert(fs, dir, n); 264 265 for (i = 0; i < n; i++, dir++) { 266 if (dir->inode_number == p->inode_number) { 267 if (p->buf) 268 v7fs_dirent_filename(p->buf, dir->name, 269 strlen(dir->name)); 270 ret = V7FS_ITERATOR_BREAK; 271 break; 272 } 273 } 274 scratch_free(fs, buf); 275 276 return ret; 277} 278 279struct lookup_parent_arg { 280 v7fs_ino_t parent_ino; 281}; 282 283static int 284can_dirmove(struct v7fs_self *fs, v7fs_ino_t from_ino, v7fs_ino_t to_ino) 285{ 286 struct v7fs_inode inode; 287 v7fs_ino_t parent; 288 int error; 289 290 /* Start dir. */ 291 if ((error = v7fs_inode_load(fs, &inode, to_ino))) 292 return error; 293 294 if (!v7fs_inode_isdir(&inode)) 295 return ENOTDIR; 296 297 /* Lookup the parent. */ 298 do { 299 struct lookup_parent_arg arg; 300 /* Search parent dir */ 301 arg.parent_ino = 0; 302 v7fs_datablock_foreach(fs, &inode, lookup_parent_from_dir_subr, 303 &arg); 304 if ((parent = arg.parent_ino) == 0) { 305 DPRINTF("***parent missing\n"); 306 return ENOENT; 307 } 308 /* Load parent dir */ 309 if ((error = v7fs_inode_load(fs, &inode, parent))) 310 return error; 311 if (parent == from_ino) { 312 DPRINTF("#%d is child dir of #%d\n", to_ino, from_ino); 313 return EINVAL; 314 } 315 } while (parent != V7FS_ROOT_INODE); 316 317 return 0; 318} 319 320static int 321lookup_parent_from_dir_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, 322 size_t sz) 323{ 324 struct lookup_parent_arg *arg = (struct lookup_parent_arg *)ctx; 325 char name[V7FS_NAME_MAX + 1]; 326 void *buf; 327 int ret = 0; 328 329 if (!(buf = scratch_read(fs, blk))) 330 return 0; 331 struct v7fs_dirent *dir = (struct v7fs_dirent *)buf; 332 size_t i, n = sz / sizeof(*dir); 333 if (!v7fs_dirent_endian_convert(fs, dir, n)) { 334 scratch_free(fs, buf); 335 return V7FS_ITERATOR_ERROR; 336 } 337 338 for (i = 0; i < n; i++, dir++) { 339 v7fs_dirent_filename(name, dir->name, strlen(dir->name)); 340 if (strncmp(dir->name, "..", V7FS_NAME_MAX) != 0) 341 continue; 342 343 arg->parent_ino = dir->inode_number; 344 ret = V7FS_ITERATOR_BREAK; 345 break; 346 } 347 348 scratch_free(fs, buf); 349 return ret; 350} 351