1235537Sgber/*-
2235537Sgber * Copyright (c) 2010-2012 Semihalf
3235537Sgber * Copyright (c) 2008, 2009 Reinoud Zandijk
4235537Sgber * All rights reserved.
5235537Sgber *
6235537Sgber * Redistribution and use in source and binary forms, with or without
7235537Sgber * modification, are permitted provided that the following conditions
8235537Sgber * are met:
9235537Sgber * 1. Redistributions of source code must retain the above copyright
10235537Sgber *    notice, this list of conditions and the following disclaimer.
11235537Sgber * 2. Redistributions in binary form must reproduce the above copyright
12235537Sgber *    notice, this list of conditions and the following disclaimer in the
13235537Sgber *    documentation and/or other materials provided with the distribution.
14235537Sgber *
15235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16235537Sgber * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17235537Sgber * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18235537Sgber * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19235537Sgber * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20235537Sgber * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21235537Sgber * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22235537Sgber * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23235537Sgber * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24235537Sgber * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25235537Sgber *
26235537Sgber * From: NetBSD: nilfs_subr.c,v 1.4 2009/07/29 17:06:57 reinoud
27235537Sgber */
28235537Sgber
29235537Sgber#include <sys/cdefs.h>
30235537Sgber__FBSDID("$FreeBSD$");
31235537Sgber
32235537Sgber#include <sys/param.h>
33235537Sgber#include <sys/systm.h>
34235537Sgber#include <sys/namei.h>
35235537Sgber#include <sys/kernel.h>
36235537Sgber#include <sys/stat.h>
37235537Sgber#include <sys/buf.h>
38235537Sgber#include <sys/bio.h>
39235537Sgber#include <sys/proc.h>
40235537Sgber#include <sys/mount.h>
41235537Sgber#include <sys/vnode.h>
42235537Sgber#include <sys/signalvar.h>
43235537Sgber#include <sys/malloc.h>
44235537Sgber#include <sys/dirent.h>
45235537Sgber#include <sys/lockf.h>
46235537Sgber
47235537Sgber#include <vm/vm.h>
48235537Sgber#include <vm/vm_extern.h>
49235537Sgber
50235537Sgber#include "nandfs_mount.h"
51235537Sgber#include "nandfs.h"
52235537Sgber#include "nandfs_subr.h"
53235537Sgber
54235537Sgberint
55235537Sgbernandfs_add_dirent(struct vnode *dvp, uint64_t ino, char *nameptr, long namelen,
56235537Sgber    uint8_t type)
57235537Sgber{
58235537Sgber	struct nandfs_node *dir_node = VTON(dvp);
59235537Sgber	struct nandfs_dir_entry *dirent, *pdirent;
60235537Sgber	uint32_t blocksize = dir_node->nn_nandfsdev->nd_blocksize;
61235537Sgber	uint64_t filesize = dir_node->nn_inode.i_size;
62235537Sgber	uint64_t inode_blks = dir_node->nn_inode.i_blocks;
63235537Sgber	uint32_t off, rest;
64235537Sgber	uint8_t *pos;
65235537Sgber	struct buf *bp;
66235537Sgber	int error;
67235537Sgber
68235537Sgber	pdirent = NULL;
69235537Sgber	bp = NULL;
70235537Sgber	if (inode_blks) {
71235537Sgber		error = nandfs_bread(dir_node, inode_blks - 1, NOCRED, 0, &bp);
72235537Sgber		if (error) {
73235537Sgber			brelse(bp);
74235537Sgber			return (error);
75235537Sgber		}
76235537Sgber
77235537Sgber		pos = bp->b_data;
78235537Sgber		off = 0;
79235537Sgber		while (off < blocksize) {
80235537Sgber			pdirent = (struct nandfs_dir_entry *) (pos + off);
81235537Sgber			if (!pdirent->rec_len) {
82235537Sgber				pdirent = NULL;
83235537Sgber				break;
84235537Sgber			}
85235537Sgber			off += pdirent->rec_len;
86235537Sgber		}
87235537Sgber
88235537Sgber		if (pdirent)
89235537Sgber			rest = pdirent->rec_len -
90235537Sgber			    NANDFS_DIR_REC_LEN(pdirent->name_len);
91235537Sgber		else
92235537Sgber			rest = blocksize;
93235537Sgber
94235537Sgber		if (rest < NANDFS_DIR_REC_LEN(namelen)) {
95235537Sgber			/* Do not update pdirent as new block is created */
96235537Sgber			pdirent = NULL;
97235537Sgber			brelse(bp);
98235537Sgber			/* Set to NULL to create new */
99235537Sgber			bp = NULL;
100235537Sgber			filesize += rest;
101235537Sgber		}
102235537Sgber	}
103235537Sgber
104235537Sgber	/* If no bp found create new */
105235537Sgber	if (!bp) {
106235537Sgber		error = nandfs_bcreate(dir_node, inode_blks, NOCRED, 0, &bp);
107235537Sgber		if (error)
108235537Sgber			return (error);
109235537Sgber		off = 0;
110235537Sgber		pos = bp->b_data;
111235537Sgber	}
112235537Sgber
113235537Sgber	/* Modify pdirent if exists */
114235537Sgber	if (pdirent) {
115235537Sgber		DPRINTF(LOOKUP, ("modify pdirent %p\n", pdirent));
116235537Sgber		/* modify last de */
117235537Sgber		off -= pdirent->rec_len;
118235537Sgber		pdirent->rec_len =
119235537Sgber		    NANDFS_DIR_REC_LEN(pdirent->name_len);
120235537Sgber		off += pdirent->rec_len;
121235537Sgber	}
122235537Sgber
123235537Sgber	/* Create new dirent */
124235537Sgber	dirent = (struct nandfs_dir_entry *) (pos + off);
125235537Sgber	dirent->rec_len = blocksize - off;
126235537Sgber	dirent->inode = ino;
127235537Sgber	dirent->name_len = namelen;
128235537Sgber	memset(dirent->name, 0, NANDFS_DIR_NAME_LEN(namelen));
129235537Sgber	memcpy(dirent->name, nameptr, namelen);
130235537Sgber	dirent->file_type = type;
131235537Sgber
132235537Sgber	filesize += NANDFS_DIR_REC_LEN(dirent->name_len);
133235537Sgber
134235537Sgber	DPRINTF(LOOKUP, ("create dir_entry '%.*s' at %p with size %x "
135235537Sgber	    "new filesize: %jx\n",
136235537Sgber	    (int)namelen, dirent->name, dirent, dirent->rec_len,
137235537Sgber	    (uintmax_t)filesize));
138235537Sgber
139235537Sgber	error = nandfs_dirty_buf(bp, 0);
140235537Sgber	if (error)
141235537Sgber		return (error);
142235537Sgber
143235537Sgber	dir_node->nn_inode.i_size = filesize;
144235537Sgber	dir_node->nn_flags |= IN_CHANGE | IN_UPDATE;
145235537Sgber	vnode_pager_setsize(dvp, filesize);
146235537Sgber
147235537Sgber	return (0);
148235537Sgber}
149235537Sgber
150235537Sgberint
151235537Sgbernandfs_remove_dirent(struct vnode *dvp, struct nandfs_node *node,
152235537Sgber    struct componentname *cnp)
153235537Sgber{
154235537Sgber	struct nandfs_node *dir_node;
155235537Sgber	struct nandfs_dir_entry *dirent, *pdirent;
156235537Sgber	struct buf *bp;
157235537Sgber	uint64_t filesize, blocknr, ino, offset;
158235537Sgber	uint32_t blocksize, limit, off;
159235537Sgber	uint16_t newsize;
160235537Sgber	uint8_t *pos;
161235537Sgber	int error, found;
162235537Sgber
163235537Sgber	dir_node = VTON(dvp);
164235537Sgber	filesize = dir_node->nn_inode.i_size;
165235537Sgber	if (!filesize)
166235537Sgber		return (0);
167235537Sgber
168235537Sgber	if (node) {
169235537Sgber		offset = node->nn_diroff;
170235537Sgber		ino = node->nn_ino;
171235537Sgber	} else {
172235537Sgber		offset = dir_node->nn_diroff;
173235537Sgber		ino = NANDFS_WHT_INO;
174235537Sgber	}
175235537Sgber
176235537Sgber	dirent = pdirent = NULL;
177235537Sgber	blocksize = dir_node->nn_nandfsdev->nd_blocksize;
178235537Sgber	blocknr = offset / blocksize;
179235537Sgber
180235537Sgber	DPRINTF(LOOKUP, ("rm direntry dvp %p node %p ino %#jx at off %#jx\n",
181235537Sgber	    dvp, node, (uintmax_t)ino, (uintmax_t)offset));
182235537Sgber
183235537Sgber	error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp);
184235537Sgber	if (error) {
185235537Sgber		brelse(bp);
186235537Sgber		return (error);
187235537Sgber	}
188235537Sgber
189235537Sgber	pos = bp->b_data;
190235537Sgber	off = 0;
191235537Sgber	found = 0;
192235537Sgber	limit = offset % blocksize;
193235537Sgber	pdirent = (struct nandfs_dir_entry *) bp->b_data;
194235537Sgber	while (off <= limit) {
195235537Sgber		dirent = (struct nandfs_dir_entry *) (pos + off);
196235537Sgber
197235537Sgber		if ((off == limit) &&
198235537Sgber		    (dirent->inode == ino)) {
199235537Sgber			found = 1;
200235537Sgber			break;
201235537Sgber		}
202235537Sgber		if (dirent->inode != 0)
203235537Sgber			pdirent = dirent;
204235537Sgber		off += dirent->rec_len;
205235537Sgber	}
206235537Sgber
207235537Sgber	if (!found) {
208235537Sgber		nandfs_error("cannot find entry to remove");
209235537Sgber		brelse(bp);
210235537Sgber		return (error);
211235537Sgber	}
212235537Sgber	DPRINTF(LOOKUP,
213235537Sgber	    ("rm dirent ino %#jx at %#x with size %#x\n",
214235537Sgber	    (uintmax_t)dirent->inode, off, dirent->rec_len));
215235537Sgber
216235537Sgber	newsize = (uintptr_t)dirent - (uintptr_t)pdirent;
217235537Sgber	newsize += dirent->rec_len;
218235537Sgber	pdirent->rec_len = newsize;
219235537Sgber	dirent->inode = 0;
220235537Sgber	error = nandfs_dirty_buf(bp, 0);
221235537Sgber	if (error)
222235537Sgber		return (error);
223235537Sgber
224235537Sgber	dir_node->nn_flags |= IN_CHANGE | IN_UPDATE;
225235537Sgber	/* If last one modify filesize */
226235537Sgber	if ((offset + NANDFS_DIR_REC_LEN(dirent->name_len)) == filesize) {
227235537Sgber		filesize = blocknr * blocksize +
228235537Sgber		    ((uintptr_t)pdirent - (uintptr_t)pos) +
229235537Sgber		    NANDFS_DIR_REC_LEN(pdirent->name_len);
230235537Sgber		dir_node->nn_inode.i_size = filesize;
231235537Sgber	}
232235537Sgber
233235537Sgber	return (0);
234235537Sgber}
235235537Sgber
236235537Sgberint
237235537Sgbernandfs_update_parent_dir(struct vnode *dvp, uint64_t newparent)
238235537Sgber{
239235537Sgber	struct nandfs_dir_entry *dirent;
240235537Sgber	struct nandfs_node *dir_node;
241235537Sgber	struct buf *bp;
242235537Sgber	int error;
243235537Sgber
244235537Sgber	dir_node = VTON(dvp);
245235537Sgber	error = nandfs_bread(dir_node, 0, NOCRED, 0, &bp);
246235537Sgber	if (error) {
247235537Sgber		brelse(bp);
248235537Sgber		return (error);
249235537Sgber	}
250235537Sgber	dirent = (struct nandfs_dir_entry *)bp->b_data;
251235537Sgber	dirent->inode = newparent;
252235537Sgber	error = nandfs_dirty_buf(bp, 0);
253235537Sgber	if (error)
254235537Sgber		return (error);
255235537Sgber
256235537Sgber	return (0);
257235537Sgber}
258235537Sgber
259235537Sgberint
260235537Sgbernandfs_update_dirent(struct vnode *dvp, struct nandfs_node *fnode,
261235537Sgber    struct nandfs_node *tnode)
262235537Sgber{
263235537Sgber	struct nandfs_node *dir_node;
264235537Sgber	struct nandfs_dir_entry *dirent;
265235537Sgber	struct buf *bp;
266235537Sgber	uint64_t file_size, blocknr;
267235537Sgber	uint32_t blocksize, off;
268235537Sgber	uint8_t *pos;
269235537Sgber	int error;
270235537Sgber
271235537Sgber	dir_node = VTON(dvp);
272235537Sgber	file_size = dir_node->nn_inode.i_size;
273235537Sgber	if (!file_size)
274235537Sgber		return (0);
275235537Sgber
276235537Sgber	DPRINTF(LOOKUP,
277235537Sgber	    ("chg direntry dvp %p ino %#jx  to in %#jx at off %#jx\n",
278235537Sgber	    dvp, (uintmax_t)tnode->nn_ino, (uintmax_t)fnode->nn_ino,
279235537Sgber	    (uintmax_t)tnode->nn_diroff));
280235537Sgber
281235537Sgber	blocksize = dir_node->nn_nandfsdev->nd_blocksize;
282235537Sgber	blocknr = tnode->nn_diroff / blocksize;
283235537Sgber	off = tnode->nn_diroff % blocksize;
284235537Sgber	error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp);
285235537Sgber	if (error) {
286235537Sgber		brelse(bp);
287235537Sgber		return (error);
288235537Sgber	}
289235537Sgber
290235537Sgber	pos = bp->b_data;
291235537Sgber	dirent = (struct nandfs_dir_entry *) (pos + off);
292235537Sgber	KASSERT((dirent->inode == tnode->nn_ino),
293235537Sgber	    ("direntry mismatch"));
294235537Sgber
295235537Sgber	dirent->inode = fnode->nn_ino;
296235537Sgber	error = nandfs_dirty_buf(bp, 0);
297235537Sgber	if (error)
298235537Sgber		return (error);
299235537Sgber
300235537Sgber	return (0);
301235537Sgber}
302235537Sgber
303235537Sgberint
304235537Sgbernandfs_init_dir(struct vnode *dvp, uint64_t ino, uint64_t parent_ino)
305235537Sgber{
306235537Sgber
307235537Sgber	if (nandfs_add_dirent(dvp, parent_ino, "..", 2, DT_DIR) ||
308235537Sgber	    nandfs_add_dirent(dvp, ino, ".", 1, DT_DIR)) {
309235537Sgber		nandfs_error("%s: cannot initialize dir ino:%jd(pino:%jd)\n",
310235537Sgber		    __func__, ino, parent_ino);
311235537Sgber		return (-1);
312235537Sgber	}
313235537Sgber	return (0);
314235537Sgber}
315