xref: /trueos/sys/fs/nandfs/nandfs_dir.c (revision ede42824618710ffa9ac08c805d8bf39bd5661ce)
1 /*-
2  * Copyright (c) 2010-2012 Semihalf
3  * Copyright (c) 2008, 2009 Reinoud Zandijk
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * From: NetBSD: nilfs_subr.c,v 1.4 2009/07/29 17:06:57 reinoud
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/namei.h>
35 #include <sys/kernel.h>
36 #include <sys/stat.h>
37 #include <sys/buf.h>
38 #include <sys/bio.h>
39 #include <sys/proc.h>
40 #include <sys/mount.h>
41 #include <sys/vnode.h>
42 #include <sys/signalvar.h>
43 #include <sys/malloc.h>
44 #include <sys/dirent.h>
45 #include <sys/lockf.h>
46 
47 #include <vm/vm.h>
48 #include <vm/vm_extern.h>
49 
50 #include "nandfs_mount.h"
51 #include "nandfs.h"
52 #include "nandfs_subr.h"
53 
54 int
nandfs_add_dirent(struct vnode * dvp,uint64_t ino,char * nameptr,long namelen,uint8_t type)55 nandfs_add_dirent(struct vnode *dvp, uint64_t ino, char *nameptr, long namelen,
56     uint8_t type)
57 {
58 	struct nandfs_node *dir_node = VTON(dvp);
59 	struct nandfs_dir_entry *dirent, *pdirent;
60 	uint32_t blocksize = dir_node->nn_nandfsdev->nd_blocksize;
61 	uint64_t filesize = dir_node->nn_inode.i_size;
62 	uint64_t inode_blks = dir_node->nn_inode.i_blocks;
63 	uint32_t off, rest;
64 	uint8_t *pos;
65 	struct buf *bp;
66 	int error;
67 
68 	pdirent = NULL;
69 	bp = NULL;
70 	if (inode_blks) {
71 		error = nandfs_bread(dir_node, inode_blks - 1, NOCRED, 0, &bp);
72 		if (error) {
73 			brelse(bp);
74 			return (error);
75 		}
76 
77 		pos = bp->b_data;
78 		off = 0;
79 		while (off < blocksize) {
80 			pdirent = (struct nandfs_dir_entry *) (pos + off);
81 			if (!pdirent->rec_len) {
82 				pdirent = NULL;
83 				break;
84 			}
85 			off += pdirent->rec_len;
86 		}
87 
88 		if (pdirent)
89 			rest = pdirent->rec_len -
90 			    NANDFS_DIR_REC_LEN(pdirent->name_len);
91 		else
92 			rest = blocksize;
93 
94 		if (rest < NANDFS_DIR_REC_LEN(namelen)) {
95 			/* Do not update pdirent as new block is created */
96 			pdirent = NULL;
97 			brelse(bp);
98 			/* Set to NULL to create new */
99 			bp = NULL;
100 			filesize += rest;
101 		}
102 	}
103 
104 	/* If no bp found create new */
105 	if (!bp) {
106 		error = nandfs_bcreate(dir_node, inode_blks, NOCRED, 0, &bp);
107 		if (error)
108 			return (error);
109 		off = 0;
110 		pos = bp->b_data;
111 	}
112 
113 	/* Modify pdirent if exists */
114 	if (pdirent) {
115 		DPRINTF(LOOKUP, ("modify pdirent %p\n", pdirent));
116 		/* modify last de */
117 		off -= pdirent->rec_len;
118 		pdirent->rec_len =
119 		    NANDFS_DIR_REC_LEN(pdirent->name_len);
120 		off += pdirent->rec_len;
121 	}
122 
123 	/* Create new dirent */
124 	dirent = (struct nandfs_dir_entry *) (pos + off);
125 	dirent->rec_len = blocksize - off;
126 	dirent->inode = ino;
127 	dirent->name_len = namelen;
128 	memset(dirent->name, 0, NANDFS_DIR_NAME_LEN(namelen));
129 	memcpy(dirent->name, nameptr, namelen);
130 	dirent->file_type = type;
131 
132 	filesize += NANDFS_DIR_REC_LEN(dirent->name_len);
133 
134 	DPRINTF(LOOKUP, ("create dir_entry '%.*s' at %p with size %x "
135 	    "new filesize: %jx\n",
136 	    (int)namelen, dirent->name, dirent, dirent->rec_len,
137 	    (uintmax_t)filesize));
138 
139 	error = nandfs_dirty_buf(bp, 0);
140 	if (error)
141 		return (error);
142 
143 	dir_node->nn_inode.i_size = filesize;
144 	dir_node->nn_flags |= IN_CHANGE | IN_UPDATE;
145 	vnode_pager_setsize(dvp, filesize);
146 
147 	return (0);
148 }
149 
150 int
nandfs_remove_dirent(struct vnode * dvp,struct nandfs_node * node,struct componentname * cnp)151 nandfs_remove_dirent(struct vnode *dvp, struct nandfs_node *node,
152     struct componentname *cnp)
153 {
154 	struct nandfs_node *dir_node;
155 	struct nandfs_dir_entry *dirent, *pdirent;
156 	struct buf *bp;
157 	uint64_t filesize, blocknr, ino, offset;
158 	uint32_t blocksize, limit, off;
159 	uint16_t newsize;
160 	uint8_t *pos;
161 	int error, found;
162 
163 	dir_node = VTON(dvp);
164 	filesize = dir_node->nn_inode.i_size;
165 	if (!filesize)
166 		return (0);
167 
168 	if (node) {
169 		offset = node->nn_diroff;
170 		ino = node->nn_ino;
171 	} else {
172 		offset = dir_node->nn_diroff;
173 		ino = NANDFS_WHT_INO;
174 	}
175 
176 	dirent = pdirent = NULL;
177 	blocksize = dir_node->nn_nandfsdev->nd_blocksize;
178 	blocknr = offset / blocksize;
179 
180 	DPRINTF(LOOKUP, ("rm direntry dvp %p node %p ino %#jx at off %#jx\n",
181 	    dvp, node, (uintmax_t)ino, (uintmax_t)offset));
182 
183 	error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp);
184 	if (error) {
185 		brelse(bp);
186 		return (error);
187 	}
188 
189 	pos = bp->b_data;
190 	off = 0;
191 	found = 0;
192 	limit = offset % blocksize;
193 	pdirent = (struct nandfs_dir_entry *) bp->b_data;
194 	while (off <= limit) {
195 		dirent = (struct nandfs_dir_entry *) (pos + off);
196 
197 		if ((off == limit) &&
198 		    (dirent->inode == ino)) {
199 			found = 1;
200 			break;
201 		}
202 		if (dirent->inode != 0)
203 			pdirent = dirent;
204 		off += dirent->rec_len;
205 	}
206 
207 	if (!found) {
208 		nandfs_error("cannot find entry to remove");
209 		brelse(bp);
210 		return (error);
211 	}
212 	DPRINTF(LOOKUP,
213 	    ("rm dirent ino %#jx at %#x with size %#x\n",
214 	    (uintmax_t)dirent->inode, off, dirent->rec_len));
215 
216 	newsize = (uintptr_t)dirent - (uintptr_t)pdirent;
217 	newsize += dirent->rec_len;
218 	pdirent->rec_len = newsize;
219 	dirent->inode = 0;
220 	error = nandfs_dirty_buf(bp, 0);
221 	if (error)
222 		return (error);
223 
224 	dir_node->nn_flags |= IN_CHANGE | IN_UPDATE;
225 	/* If last one modify filesize */
226 	if ((offset + NANDFS_DIR_REC_LEN(dirent->name_len)) == filesize) {
227 		filesize = blocknr * blocksize +
228 		    ((uintptr_t)pdirent - (uintptr_t)pos) +
229 		    NANDFS_DIR_REC_LEN(pdirent->name_len);
230 		dir_node->nn_inode.i_size = filesize;
231 	}
232 
233 	return (0);
234 }
235 
236 int
nandfs_update_parent_dir(struct vnode * dvp,uint64_t newparent)237 nandfs_update_parent_dir(struct vnode *dvp, uint64_t newparent)
238 {
239 	struct nandfs_dir_entry *dirent;
240 	struct nandfs_node *dir_node;
241 	struct buf *bp;
242 	int error;
243 
244 	dir_node = VTON(dvp);
245 	error = nandfs_bread(dir_node, 0, NOCRED, 0, &bp);
246 	if (error) {
247 		brelse(bp);
248 		return (error);
249 	}
250 	dirent = (struct nandfs_dir_entry *)bp->b_data;
251 	dirent->inode = newparent;
252 	error = nandfs_dirty_buf(bp, 0);
253 	if (error)
254 		return (error);
255 
256 	return (0);
257 }
258 
259 int
nandfs_update_dirent(struct vnode * dvp,struct nandfs_node * fnode,struct nandfs_node * tnode)260 nandfs_update_dirent(struct vnode *dvp, struct nandfs_node *fnode,
261     struct nandfs_node *tnode)
262 {
263 	struct nandfs_node *dir_node;
264 	struct nandfs_dir_entry *dirent;
265 	struct buf *bp;
266 	uint64_t file_size, blocknr;
267 	uint32_t blocksize, off;
268 	uint8_t *pos;
269 	int error;
270 
271 	dir_node = VTON(dvp);
272 	file_size = dir_node->nn_inode.i_size;
273 	if (!file_size)
274 		return (0);
275 
276 	DPRINTF(LOOKUP,
277 	    ("chg direntry dvp %p ino %#jx  to in %#jx at off %#jx\n",
278 	    dvp, (uintmax_t)tnode->nn_ino, (uintmax_t)fnode->nn_ino,
279 	    (uintmax_t)tnode->nn_diroff));
280 
281 	blocksize = dir_node->nn_nandfsdev->nd_blocksize;
282 	blocknr = tnode->nn_diroff / blocksize;
283 	off = tnode->nn_diroff % blocksize;
284 	error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp);
285 	if (error) {
286 		brelse(bp);
287 		return (error);
288 	}
289 
290 	pos = bp->b_data;
291 	dirent = (struct nandfs_dir_entry *) (pos + off);
292 	KASSERT((dirent->inode == tnode->nn_ino),
293 	    ("direntry mismatch"));
294 
295 	dirent->inode = fnode->nn_ino;
296 	error = nandfs_dirty_buf(bp, 0);
297 	if (error)
298 		return (error);
299 
300 	return (0);
301 }
302 
303 int
nandfs_init_dir(struct vnode * dvp,uint64_t ino,uint64_t parent_ino)304 nandfs_init_dir(struct vnode *dvp, uint64_t ino, uint64_t parent_ino)
305 {
306 
307 	if (nandfs_add_dirent(dvp, parent_ino, "..", 2, DT_DIR) ||
308 	    nandfs_add_dirent(dvp, ino, ".", 1, DT_DIR)) {
309 		nandfs_error("%s: cannot initialize dir ino:%jd(pino:%jd)\n",
310 		    __func__, ino, parent_ino);
311 		return (-1);
312 	}
313 	return (0);
314 }
315