1 /* $NetBSD: v7fs_file.c,v 1.6 2014/12/29 15:28:58 hannken Exp $ */
4 * Copyright (c) 2011 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
32 #if HAVE_NBTOOL_CONFIG_H
33 #include "nbtool_config.h"
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
42 #include <sys/param.h>
44 #include <sys/systm.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"
59 #ifdef V7FS_FILE_DEBUG
60 #define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args)
62 #define DPRINTF(fmt, args...) ((void)0)
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);
69 v7fs_file_lookup_by_name(struct v7fs_self
*fs
, struct v7fs_inode
*parent_dir
,
70 const char *name
, v7fs_ino_t
*ino
)
72 char filename
[V7FS_NAME_MAX
+ 1];
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' */
83 v7fs_dirent_filename(filename
, name
);
85 DPRINTF("%s(%s) dir=%d\n", filename
, name
, parent_dir
->inode_number
);
87 struct v7fs_lookup_arg lookup_arg
= { .name
= filename
,
89 if ((error
= v7fs_datablock_foreach(fs
, parent_dir
, lookup_subr
,
90 &lookup_arg
)) != V7FS_ITERATOR_BREAK
) {
91 DPRINTF("not found.\n");
95 *ino
= lookup_arg
.inode_number
;
96 DPRINTF("done. ino=%d\n", *ino
);
102 lookup_subr(struct v7fs_self
*fs
, void *ctx
, v7fs_daddr_t blk
, size_t sz
)
104 struct v7fs_lookup_arg
*p
= (struct v7fs_lookup_arg
*)ctx
;
105 struct v7fs_dirent
*dir
;
106 const char *name
= p
->name
;
111 if (!(buf
= scratch_read(fs
, blk
)))
114 dir
= (struct v7fs_dirent
*)buf
;
115 n
= sz
/ sizeof(*dir
);
116 v7fs_dirent_endian_convert(fs
, dir
, n
);
118 for (i
= 0; i
< n
; i
++, dir
++) {
119 if (dir
->inode_number
< 1) {
120 DPRINTF("*** bad inode #%d ***\n", dir
->inode_number
);
124 if (strncmp((const char *)dir
->name
, name
, V7FS_NAME_MAX
) == 0)
126 p
->inode_number
= dir
->inode_number
;
127 ret
= V7FS_ITERATOR_BREAK
; /* found */
131 scratch_free(fs
, buf
);
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
)
140 struct v7fs_inode inode
;
141 char filename
[V7FS_NAME_MAX
+ 1];
142 struct v7fs_dirent
*dir
;
145 /* Truncate filename. */
146 v7fs_dirent_filename(filename
, srcname
);
147 DPRINTF("%s(%s)\n", filename
, srcname
);
149 /* Check filename. */
150 if (v7fs_file_lookup_by_name(fs
, parent_dir
, filename
, ino
) == 0) {
151 DPRINTF("%s exists\n", filename
);
156 if ((error
= v7fs_inode_allocate(fs
, ino
)))
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
;
166 inode
.ctime
= attr
->ctime
;
168 inode
.mtime
= attr
->mtime
;
170 inode
.atime
= attr
->atime
;
172 switch (inode
.mode
& V7FS_IFMT
) {
174 DPRINTF("Can't allocate %o type.\n", inode
.mode
);
175 v7fs_inode_deallocate(fs
, *ino
);
181 inode
.device
= attr
->device
;
182 inode
.addr
[0] = inode
.device
;
194 inode
.nlink
= 2; /* . + .. */
195 if ((error
= v7fs_datablock_expand(fs
, &inode
, sizeof(*dir
) * 2
197 v7fs_inode_deallocate(fs
, *ino
);
200 v7fs_daddr_t blk
= inode
.addr
[0];
202 if (!(buf
= scratch_read(fs
, blk
))) {
203 v7fs_inode_deallocate(fs
, *ino
);
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
);
215 scratch_free(fs
, buf
);
219 v7fs_inode_writeback(fs
, &inode
);
221 /* Link this inode to parent directory. */
222 if ((error
= v7fs_directory_add_entry(fs
, parent_dir
, *ino
, filename
)))
224 DPRINTF("can't add dirent.\n");
232 v7fs_file_deallocate(struct v7fs_self
*fs
, struct v7fs_inode
*parent_dir
,
236 struct v7fs_inode inode
;
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
);
244 DPRINTF("%s->#%d\n", name
, ino
);
245 if ((error
= v7fs_inode_load(fs
, &inode
, ino
)))
248 if (v7fs_inode_isdir(&inode
)) {
249 char filename
[V7FS_NAME_MAX
+ 1];
250 v7fs_dirent_filename(filename
, name
);
252 if (strncmp(filename
, "..", V7FS_NAME_MAX
) == 0) {
253 DPRINTF("can not remove '..'\n");
254 return EINVAL
; /* t_vnops rename_dotdot */
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)*/
262 error
= v7fs_datablock_size_change(fs
, 0, &inode
);
265 inode
.nlink
= 0; /* remove this. */
267 /* Decrement reference count. */
268 --inode
.nlink
; /* regular file. */
269 DPRINTF("%s nlink=%d\n", name
, inode
.nlink
);
273 if ((error
= v7fs_directory_remove_entry(fs
, parent_dir
, name
)))
275 DPRINTF("remove dirent\n");
277 v7fs_inode_writeback(fs
, &inode
);
283 v7fs_directory_add_entry(struct v7fs_self
*fs
, struct v7fs_inode
*parent_dir
,
284 v7fs_ino_t ino
, const char *srcname
)
286 struct v7fs_inode inode
;
287 struct v7fs_dirent
*dir
;
291 char filename
[V7FS_NAME_MAX
+ 1];
293 /* Truncate filename. */
294 v7fs_dirent_filename(filename
, srcname
);
295 DPRINTF("%s(%s) %d\n", filename
, srcname
, ino
);
298 if ((error
= v7fs_inode_load(fs
, &inode
, ino
)))
301 /* Expand datablock. */
302 if ((error
= v7fs_datablock_expand(fs
, parent_dir
, sizeof(*dir
))))
305 /* Read last entry. */
306 if (!(blk
= v7fs_datablock_last(fs
, parent_dir
,
307 v7fs_inode_filesize(parent_dir
))))
310 /* Load dirent block. This vnode(parent dir) is locked by VFS layer. */
311 if (!(buf
= scratch_read(fs
, blk
)))
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;
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
))
324 scratch_free(fs
, buf
);
326 if (v7fs_inode_isdir(&inode
)) {
328 v7fs_inode_writeback(fs
, parent_dir
);
331 DPRINTF("done. (dirent size=%dbyte)\n", parent_dir
->filesize
);
337 v7fs_directory_remove_entry(struct v7fs_self
*fs
, struct v7fs_inode
*parent_dir
,
340 struct v7fs_inode inode
;
342 struct v7fs_dirent lastdirent
;
343 v7fs_daddr_t lastblk
;
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
);
355 if (!(buf
= scratch_read(fs
, lastblk
)))
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
);
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
)
369 /* Contract dirent entries. */
370 v7fs_datablock_contract(fs
, parent_dir
, sizeof(lastdirent
));
371 DPRINTF("done. (dirent size=%dbyte)\n", parent_dir
->filesize
);
374 if ((error
= v7fs_inode_load(fs
, &inode
, lookup_arg
.inode_number
)))
377 if (v7fs_inode_isdir(&inode
)) {
379 v7fs_inode_writeback(fs
, parent_dir
);
386 remove_subr(struct v7fs_self
*fs
, void *ctx
, v7fs_daddr_t blk
, size_t sz
)
388 struct v7fs_lookup_arg
*p
= (struct v7fs_lookup_arg
*)ctx
;
389 struct v7fs_dirent
*dir
;
394 DPRINTF("match start blk=%x\n", blk
);
395 if (!(buf
= scratch_read(fs
, blk
)))
398 dir
= (struct v7fs_dirent
*)buf
;
400 for (i
= 0; i
< sz
/ sizeof(*dir
); i
++, dir
++) {
401 DPRINTF("%d\n", V7FS_VAL16(fs
, dir
->inode_number
));
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 */
408 if (!fs
->io
.write(fs
->io
.cookie
, buf
, blk
))
411 ret
= V7FS_ITERATOR_BREAK
;
415 scratch_free(fs
, buf
);