]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/fs/nandfs/nandfs_dir.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / fs / nandfs / nandfs_dir.c
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
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
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
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
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
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 }