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
54int
55nandfs_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
150int
151nandfs_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
236int
237nandfs_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
259int
260nandfs_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
303int
304nandfs_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