(nnpfs_block_open): better cleanup on failure
[arla.git] / nnpfs / bsd / nnpfs_blocks.c
blob42a989398fd7cfd9e61522bdf2592b704f9d5693
1 /*
2 * Copyright (c) 2005-2007, Stockholms Universitet
3 * (Stockholm University, Stockholm Sweden)
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * 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.
17 * 3. Neither the name of the university nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
34 /* $Id$ */
36 #include <nnpfs/nnpfs_locl.h>
37 #include <nnpfs/nnpfs_fs.h>
38 #include <nnpfs/nnpfs_dev.h>
39 #include <nnpfs/nnpfs_deb.h>
42 * return true if block is in cache
45 int
46 nnpfs_block_have_p(struct nnpfs_node *node, uint64_t offset)
48 struct nnpfs_cache_handle *handle = &node->data;
49 uint32_t index = nnpfs_block_index(offset);
50 uint32_t maskno = nnpfs_block_masknumber(index);
52 nnpfs_assert(nnpfs_offset(offset) == offset);
54 if (handle->nmasks == 0)
55 return 0;
57 if (maskno >= handle->nmasks)
58 return 0;
60 if (handle->nmasks == 1)
61 return (handle->masks.first & nnpfs_block_mask(index));
63 return (handle->masks.list[maskno] & nnpfs_block_mask(index));
67 * mark block at offset as present in cache
69 * XXX assert on the bit being changed?
72 static int
73 nnpfs_block_set_have(struct nnpfs_node *node, uint64_t offset, int val)
75 struct nnpfs_cache_handle *handle = &node->data;
76 uint32_t index = nnpfs_block_index(offset);
77 uint32_t maskno = nnpfs_block_masknumber(index);
78 uint32_t mask = nnpfs_block_mask(index);
79 uint32_t *slot;
81 nnpfs_assert(nnpfs_offset(offset) == offset);
83 if (maskno == 0 && handle->nmasks <= 1) {
84 handle->nmasks = 1;
85 slot = &handle->masks.first;
86 } else {
87 if (maskno >= handle->nmasks) {
88 int n = maskno + NNPFS_NMASKS - (maskno % NNPFS_NMASKS);
89 int size = n * sizeof(uint32_t);
90 uint32_t *new;
92 nnpfs_assert(val);
94 new = nnpfs_alloc(size, M_NNPFS_BLOCKS);
95 nnpfs_debug_assert(new);
96 if (!new)
97 return ENOMEM;
99 if (handle->nmasks == 1) {
100 new[0] = handle->masks.first;
101 } else if (handle->nmasks > 1) {
102 memcpy(new, handle->masks.list,
103 handle->nmasks * sizeof(uint32_t));
104 nnpfs_free(handle->masks.list, handle->nmasks * sizeof(uint32_t),
105 M_NNPFS_BLOCKS);
108 memset(&new[handle->nmasks], 0,
109 (n - handle->nmasks) * sizeof(uint32_t));
110 handle->nmasks = n;
111 handle->masks.list = new;
113 slot = &handle->masks.list[maskno];
116 if (val)
117 *slot |= mask;
118 else
119 *slot &= ~mask;
121 return 0;
125 * mark block at offset as present in cache
129 nnpfs_block_setvalid(struct nnpfs_node *node, uint64_t offset)
131 return nnpfs_block_set_have(node, offset, TRUE);
135 * mark block at offset as not present in cache
138 void
139 nnpfs_block_setinvalid(struct nnpfs_node *node, uint64_t offset)
141 (void)nnpfs_block_set_have(node, offset, FALSE);
144 static void
145 nnpfs_block_foreach_int(struct nnpfs_node *node,
146 nnpfs_block_callback_t fun,
147 void *data,
148 uint64_t base_offset,
149 int32_t mask)
151 uint32_t tmp_mask = 1;
152 int i;
154 if (!mask)
155 return;
157 for (i = 0; i < 32; i++) {
158 if (mask & tmp_mask) {
159 fun(node, base_offset + i * nnpfs_blocksize, data);
160 mask -= tmp_mask;
161 if (!mask)
162 return;
165 tmp_mask = tmp_mask << 1;
170 * call callback for every block present in cache
173 void
174 nnpfs_block_foreach(struct nnpfs_node *node,
175 nnpfs_block_callback_t fun,
176 void *data)
178 struct nnpfs_cache_handle *handle = &node->data;
179 int i;
181 if (handle->nmasks == 0)
182 return;
184 if (handle->nmasks == 1) {
185 nnpfs_block_foreach_int(node, fun, data, 0, handle->masks.first);
186 return;
189 for (i = 0; i < handle->nmasks; i++)
190 nnpfs_block_foreach_int(node, fun, data, i * 32 * nnpfs_blocksize,
191 handle->masks.list[i]);
195 * Foreach callback for nnpfs_block_truncate()
198 static void
199 truncate_callback(struct nnpfs_node *node, uint64_t offset, void *data)
201 uint64_t *size = (uint64_t *)data;
202 if (*size <= offset && offset > 0)
203 (void)nnpfs_block_set_have(node, offset, FALSE);
207 * Forget all blocks beyond `sizeĀ“ for `node'
210 void
211 nnpfs_block_truncate(struct nnpfs_node *node, uint64_t size)
213 nnpfs_block_foreach(node, truncate_callback, &size);
217 * free all handle internal resources
220 void
221 nnpfs_block_free_all(struct nnpfs_node *node)
223 struct nnpfs_cache_handle *handle = &node->data;
224 if (handle->nmasks > 1) {
225 nnpfs_free(handle->masks.list, handle->nmasks * sizeof(uint32_t),
226 M_NNPFS_BLOCKS);
227 handle->masks.list = NULL;
228 } else {
229 handle->masks.first = 0;
232 handle->nmasks = 0;
236 * return true if we have no data
240 nnpfs_block_empty(struct nnpfs_node *node)
242 struct nnpfs_cache_handle *handle = &node->data;
243 int i;
245 if (handle->nmasks == 0)
246 return 1;
248 if (handle->nmasks == 1) {
249 if (handle->masks.first == 0)
250 return 1;
251 return 0;
254 for (i = 0; i < handle->nmasks; i++)
255 if (handle->masks.list[i] != 0)
256 return 0;
258 return 1;
261 static int
262 nnpfs_block_extend_int(struct nnpfs_node *node, struct vnode *vp)
264 struct nnpfs_vfs_vattr va;
265 int ret;
267 VATTR_INIT(&va);
268 nnpfs_set_va_size(&va, nnpfs_blocksize);
269 ret = nnpfs_vnode_setattr(vp, &va, NNPFS_FROM_XNODE(node)->ctx);
270 nnpfs_debug_assert(!ret);
271 return ret;
275 * Extend an existing block to full block size.
278 static int
279 nnpfs_block_extend(struct nnpfs_node *node, uint64_t offset)
281 struct vnode *vp;
282 int ret;
284 nnpfs_assert(nnpfs_block_have_p(node, offset));
286 ret = nnpfs_block_open(node, offset, O_RDWR|FWRITE, &vp);
287 if (!ret) {
288 nnpfs_assert(vp);
289 ret = nnpfs_block_extend_int(node, vp);
290 nnpfs_block_close(vp, 1);
293 if (ret)
294 printf("nnpfs_block_extend: failed at offset 0x%llx: %d\n",
295 (unsigned long long)offset, ret);
297 return ret;
301 * a handy implementation of open()
303 * nnpfs_block_close() on success.
306 static int
307 open_file(const char *name, int fmode,
308 nnpfs_vfs_context ctx, struct vnode **vp)
310 int error;
312 #ifdef __APPLE__ /* XXX */
313 error = vnode_open(name, fmode, S_IRUSR|S_IWUSR, 0, vp, ctx);
314 #else
316 struct nameidata nd, *ndp = &nd;
317 d_thread_t *p = nnpfs_curproc();
318 nnpfs_kernel_cred cred = nnpfs_proc_to_cred(p);
320 NDINIT(ndp, LOOKUP, 0, UIO_SYSSPACE, name,
321 nnpfs_vfs_context_proc(ctx));
323 error = namei(ndp);
324 if (error != 0)
325 return error;
327 *vp = ndp->ni_vp;
329 #if defined(__FreeBSD__) && __FreeBSD_version >= 502000
330 error = VOP_OPEN(*vp, fmode & (FWRITE|FREAD), cred, p, -1);
331 #else
332 error = VOP_OPEN(*vp, fmode & (FWRITE|FREAD), cred, p);
333 #endif
334 if (error)
335 nnpfs_vletgo(*vp);
337 #endif
339 return error;
343 * open indicated cache block file. needs to be closed by caller.
347 nnpfs_block_open(struct nnpfs_node *node, uint64_t offset, int flags,
348 struct vnode **vpp)
350 char cachename[NNPFS_CACHE_PATH_SIZE];
351 uint64_t blockindex = nnpfs_block_index(offset);
352 uint32_t id = node->index;
353 struct nnpfs *nnpfsp = NNPFS_FROM_XNODE(node);
354 off_t eof = nnpfs_vattr_get_size(&node->attr);
355 int ret;
357 NNPFSDEB(XDEBNODE, ("nnpfs_block_open\n"));
359 nnpfs_assert(nnpfsp);
361 nnpfs_assert(nnpfs_block_have_p(node, offset)
362 || (flags & O_CREAT));
364 if (nnpfs_vnode_isdir(XNODE_TO_VNODE(node)))
365 ret = snprintf(cachename, sizeof(cachename),
366 NNPFS_CACHE_DIR_PATH,
367 id / 0x100, id % 0x100);
368 else
369 ret = snprintf(cachename, sizeof(cachename),
370 NNPFS_CACHE_FILE_PATH,
371 id / 0x100, id % 0x100,
372 (unsigned long long)blockindex);
374 nnpfs_assert(ret > 0 && ret < sizeof(cachename)); /* XXX */
376 ret = open_file(cachename, flags, nnpfsp->ctx, vpp);
377 nnpfs_debug_assert(!ret);
378 if (ret)
379 return ret;
381 /* blocks in the middle of the file should be of full length */
382 if ((flags & O_CREAT) && offset < nnpfs_offset(eof)) {
383 ret = nnpfs_block_extend_int(node, *vpp);
384 nnpfs_debug_assert(!ret);
385 if (ret)
386 nnpfs_block_close(*vpp,
387 ((flags & FWRITE) == FWRITE) ? 1 : 0);
390 NNPFSDEB(XDEBNODE, ("nnpfs_block_open -> %d\n", ret));
391 return ret;
394 void
395 nnpfs_block_close(struct vnode *vp, int rw)
397 #ifdef __APPLE__
398 vnode_close(vp, 0, NULL);
399 #else
400 d_thread_t *p = nnpfs_curproc();
401 struct ucred *cred = NULL;
403 VOP_CLOSE(vp, rw ? FWRITE : FREAD, cred, p);
404 nnpfs_vletgo(vp);
405 #endif
409 * Create the indicated block and mark it as present in cache.
411 * Intended for writes beyond EOF.
415 nnpfs_block_create(struct nnpfs_node *node, uint64_t offset)
417 struct nnpfs_message_appenddata msg;
418 struct nnpfs *nnpfsp = NNPFS_FROM_XNODE(node);
419 off_t eof = nnpfs_vattr_get_size(&node->attr);
420 struct vnode *vp;
421 int ret;
423 nnpfs_assert(!nnpfs_block_have_p(node, offset));
424 nnpfs_assert(!nnpfs_vnode_isdir(XNODE_TO_VNODE(node)));
426 NNPFSDEB(XDEBNODE, ("nnpfs_block_create: %lx @0x%llx\n",
427 (unsigned long)node, (unsigned long long )offset));
429 ret = nnpfs_block_setvalid(node, offset);
430 if (ret) {
431 nnpfs_debug_assert(0);
432 return ret;
435 ret = nnpfs_block_open(node, offset, O_CREAT|FWRITE, &vp);
436 if (!ret) {
437 nnpfs_assert(vp);
438 nnpfs_block_close(vp, 1);
441 /* extend previously last block to full length */
442 if (!ret && eof < offset) {
443 uint64_t prevoff = nnpfs_offset(eof);
444 if (nnpfs_block_have_p(node, prevoff))
445 ret = nnpfs_block_extend(node, prevoff);
448 nnpfs_debug_assert(!ret);
450 if (ret) {
451 /* XXX roll back file changes? */
452 nnpfs_block_setinvalid(node, offset);
453 return ret;
456 while (nnpfsp->appendquota < nnpfs_blocksize
457 && nnpfsp->status & CHANNEL_OPENED) {
458 int waiting = (nnpfsp->status & NNPFS_QUOTAWAIT);
459 nnpfsp->status |= NNPFS_QUOTAWAIT;
460 /* XXX */
461 (void)nnpfs_dev_msleep(nnpfsp, (caddr_t)&nnpfsp->appendquota,
462 (PZERO + 1), "nnpfsquota");
463 if (!waiting)
464 nnpfsp->status &= ~NNPFS_QUOTAWAIT;
467 if ((nnpfsp->status & CHANNEL_OPENED) == 0)
468 return ENODEV;
470 nnpfsp->appendquota -= nnpfs_blocksize;
471 nnpfs_assert(nnpfsp->appendquota >= 0);
473 msg.header.opcode = NNPFS_MSG_APPENDDATA;
474 msg.handle = node->handle;
475 msg.offset = offset;
477 /* XXX currently no cleanup on failed send, hope it's just a devclose */
478 return nnpfs_message_send(nnpfsp->fd, &msg.header, sizeof(msg));