xref: /minix/sys/fs/v7fs/v7fs_file.c (revision 0a6a1f1d)
1 /*	$NetBSD: v7fs_file.c,v 1.6 2014/12/29 15:28:58 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.6 2014/12/29 15:28:58 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 
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
v7fs_file_lookup_by_name(struct v7fs_self * fs,struct v7fs_inode * parent_dir,const char * name,v7fs_ino_t * ino)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
lookup_subr(struct v7fs_self * fs,void * ctx,v7fs_daddr_t blk,size_t sz)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
v7fs_file_allocate(struct v7fs_self * fs,struct v7fs_inode * parent_dir,const char * srcname,struct v7fs_fileattr * attr,v7fs_ino_t * ino)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
v7fs_file_deallocate(struct v7fs_self * fs,struct v7fs_inode * parent_dir,const char * name)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 		error = v7fs_datablock_size_change(fs, 0, &inode);
263 		if (error)
264 			return error;
265 		inode.nlink = 0;	/* remove this. */
266 	} else {
267 		/* Decrement reference count. */
268 		--inode.nlink;	/* regular file. */
269 		DPRINTF("%s nlink=%d\n", name, inode.nlink);
270 	}
271 
272 
273 	if ((error = v7fs_directory_remove_entry(fs, parent_dir, name)))
274 		return error;
275 	DPRINTF("remove dirent\n");
276 
277 	v7fs_inode_writeback(fs, &inode);
278 
279 	return 0;
280 }
281 
282 int
v7fs_directory_add_entry(struct v7fs_self * fs,struct v7fs_inode * parent_dir,v7fs_ino_t ino,const char * srcname)283 v7fs_directory_add_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir,
284     v7fs_ino_t ino, const char *srcname)
285 {
286 	struct v7fs_inode inode;
287 	struct v7fs_dirent *dir;
288 	int error = 0;
289 	v7fs_daddr_t blk;
290 	void *buf;
291 	char filename[V7FS_NAME_MAX + 1];
292 
293 	/* Truncate filename. */
294 	v7fs_dirent_filename(filename, srcname);
295 	DPRINTF("%s(%s) %d\n", filename, srcname, ino);
296 
297 	/* Target inode */
298 	if ((error = v7fs_inode_load(fs, &inode, ino)))
299 		return error;
300 
301 	/* Expand datablock. */
302 	if ((error = v7fs_datablock_expand(fs, parent_dir, sizeof(*dir))))
303 		return error;
304 
305 	/* Read last entry. */
306 	if (!(blk = v7fs_datablock_last(fs, parent_dir,
307 	    v7fs_inode_filesize(parent_dir))))
308 		return EIO;
309 
310 	/* Load dirent block. This vnode(parent dir) is locked by VFS layer. */
311 	if (!(buf = scratch_read(fs, blk)))
312 		return EIO;
313 
314 	size_t sz = v7fs_inode_filesize(parent_dir);
315 	sz = V7FS_RESIDUE_BSIZE(sz);	/* last block payload. */
316 	int n = sz / sizeof(*dir) - 1;
317 	/* Add dirent. */
318 	dir = (struct v7fs_dirent *)buf;
319 	dir[n].inode_number = V7FS_VAL16(fs, ino);
320 	memcpy((char *)dir[n].name, filename, V7FS_NAME_MAX);
321 	/* Write back datablock */
322 	if (!fs->io.write(fs->io.cookie, buf, blk))
323 		error = EIO;
324 	scratch_free(fs, buf);
325 
326 	if (v7fs_inode_isdir(&inode)) {
327 		parent_dir->nlink++;
328 		v7fs_inode_writeback(fs, parent_dir);
329 	}
330 
331 	DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize);
332 
333 	return error;
334 }
335 
336 int
v7fs_directory_remove_entry(struct v7fs_self * fs,struct v7fs_inode * parent_dir,const char * name)337 v7fs_directory_remove_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir,
338     const char *name)
339 {
340 	struct v7fs_inode inode;
341 	int error;
342 	struct v7fs_dirent lastdirent;
343 	v7fs_daddr_t lastblk;
344 	size_t sz, lastsz;
345 	v7fs_off_t pos;
346 	void *buf;
347 
348 	/* Setup replaced entry. */
349 	sz = parent_dir->filesize;
350 	lastblk = v7fs_datablock_last(fs, parent_dir,
351 	    v7fs_inode_filesize(parent_dir));
352 	lastsz = V7FS_RESIDUE_BSIZE(sz);
353 	pos = lastsz - sizeof(lastdirent);
354 
355 	if (!(buf = scratch_read(fs, lastblk)))
356 		return EIO;
357 	lastdirent = *((struct v7fs_dirent *)((uint8_t *)buf + pos));
358 	scratch_free(fs, buf);
359 	DPRINTF("last dirent=%d %s pos=%d\n",
360 	    V7FS_VAL16(fs, lastdirent.inode_number), lastdirent.name, pos);
361 
362 	struct v7fs_lookup_arg lookup_arg =
363 	    { .name = name, .replace = &lastdirent/*disk endian */ };
364 	/* Search entry that removed. replace it to last dirent. */
365 	if ((error = v7fs_datablock_foreach(fs, parent_dir, remove_subr,
366 	    &lookup_arg)) != V7FS_ITERATOR_BREAK)
367 		return ENOENT;
368 
369 	/* Contract dirent entries. */
370 	v7fs_datablock_contract(fs, parent_dir, sizeof(lastdirent));
371 	DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize);
372 
373 	/* Target inode */
374 	if ((error = v7fs_inode_load(fs, &inode, lookup_arg.inode_number)))
375 		return error;
376 
377 	if (v7fs_inode_isdir(&inode)) {
378 		parent_dir->nlink--;
379 		v7fs_inode_writeback(fs, parent_dir);
380 	}
381 
382 	return 0;
383 }
384 
385 static int
remove_subr(struct v7fs_self * fs,void * ctx,v7fs_daddr_t blk,size_t sz)386 remove_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz)
387 {
388 	struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx;
389 	struct v7fs_dirent *dir;
390 	void *buf;
391 	size_t i;
392 	int ret = 0;
393 
394 	DPRINTF("match start blk=%x\n", blk);
395 	if (!(buf = scratch_read(fs, blk)))
396 		return EIO;
397 
398 	dir = (struct v7fs_dirent *)buf;
399 
400 	for (i = 0; i < sz / sizeof(*dir); i++, dir++) {
401 		DPRINTF("%d\n", V7FS_VAL16(fs, dir->inode_number));
402 		if (strncmp(p->name,
403 			(const char *)dir->name, V7FS_NAME_MAX) == 0) {
404 			p->inode_number = V7FS_VAL16(fs, dir->inode_number);
405 			/* Replace to last dirent. */
406 			*dir = *(p->replace); /* disk endian */
407 			/* Write back. */
408 			if (!fs->io.write(fs->io.cookie, buf, blk))
409 				ret = EIO;
410 			else
411 				ret = V7FS_ITERATOR_BREAK;
412 			break;
413 		}
414 	}
415 	scratch_free(fs, buf);
416 
417 	return ret;
418 }
419