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