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