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