1 /* Blue Sky: File Systems in the Cloud
3 * Copyright (C) 2009 The Regents of the University of California
4 * Written by Michael Vrable <mvrable@cs.ucsd.edu>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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 * 3. Neither the name of the University nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * This is sample code generated by rpcgen.
33 * These are only templates and you can use them
34 * as a guideline for developing your own functions.
37 #include "nfs3_prot.h"
42 #define NFS_BLOCKSIZE 32768
43 #define NFS_MAXSIZE (1 << 20)
45 /* Check that a string is a valid file name. We require that it be valid
46 * UTF-8, that it not be empty, and that it not contain embedded forward
47 * slashes. Also checks that the length of the string is not more than the
48 * maximum allowed length. This function does allow the names "." and "..".
49 * Returns TRUE if the string is allowed as a filename. */
50 gboolean validate_filename(const char *filename)
52 if (filename == NULL || filename[0] == '\0')
54 if (strlen(filename) > 255)
56 if (!g_utf8_validate(filename, -1, NULL))
58 if (strchr(filename, '/') != NULL)
63 /* Arrange for a reference to an inode to be dropped when the RPC request
65 void schedule_inode_unref(RPCRequest *req, BlueSkyInode *inode)
67 struct cleanup_list *c = g_new(struct cleanup_list, 1);
68 c->func = (void (*)(void *))bluesky_inode_unref;
70 c->next = req->cleanup;
74 /* Look up a BlueSkyInode given an NFS filehandle. Returns NULL if the
75 * filehandle is invalid. */
76 BlueSkyInode *lookup_fh(RPCRequest *req, nfs_fh3 *fh)
78 BlueSkyInode *inode = NULL;
79 if (fh->data.data_len == 8) {
80 uint64_t inum = GUINT64_FROM_BE(*(uint64_t *)(fh->data.data_val));
81 inode = bluesky_get_inode(fs, inum);
83 schedule_inode_unref(req, inode);
88 int64_t decode_nfstime3(nfstime3 *time)
90 int64_t result = (int64_t)time->seconds * 1000000;
91 result += time->nseconds / 1000;
95 void set_attributes(BlueSkyInode *inode, sattr3 *attributes)
97 int64_t now = bluesky_get_current_time();
99 if (attributes->mode.set) {
100 inode->mode = attributes->mode.set_uint32_u.val;
103 if (attributes->uid.set) {
104 inode->uid = attributes->uid.set_uint32_u.val;
107 if (attributes->gid.set) {
108 inode->gid = attributes->gid.set_uint32_u.val;
111 if (attributes->size.set) {
112 if (inode->type == BLUESKY_REGULAR) {
113 bluesky_file_truncate(inode, attributes->size.set_uint64_u.val);
118 switch (attributes->atime.set) {
121 case SET_TO_SERVER_TIME:
124 case SET_TO_CLIENT_TIME:
125 inode->atime = decode_nfstime3(&attributes->atime.set_time_u.time);
129 switch (attributes->mtime.set) {
132 case SET_TO_SERVER_TIME:
135 case SET_TO_CLIENT_TIME:
136 inode->mtime = decode_nfstime3(&attributes->mtime.set_time_u.time);
140 bluesky_inode_update_ctime(inode, FALSE);
143 /* Copy inode attributes into NFS response. The BlueSkyInode should be locked
145 void encode_fattr3(struct fattr3 *result, BlueSkyInode *inode)
147 result->type = inode->type;
148 result->mode = inode->mode;
149 result->nlink = inode->nlink;
150 result->uid = inode->uid;
151 result->gid = inode->gid;
152 result->size = inode->size;
154 result->rdev.major = 0;
155 result->rdev.minor = 0;
157 result->fileid = inode->inum;
158 result->atime.seconds = inode->atime / 1000000;
159 result->atime.nseconds = (inode->atime % 1000000) * 1000;
160 result->mtime.seconds = inode->mtime / 1000000;
161 result->mtime.nseconds = (inode->mtime % 1000000) * 1000;
162 result->ctime.seconds = inode->ctime / 1000000;
163 result->ctime.nseconds = (inode->ctime % 1000000) * 1000;
165 switch (inode->type) {
166 case BLUESKY_SYMLINK:
167 result->size = strlen(inode->symlink_contents);
174 void encode_pre_wcc(struct wcc_data *wcc, BlueSkyInode *inode)
176 wcc->before.present = TRUE;
177 wcc->before.pre_op_attr_u.attributes.size = inode->size;
178 wcc->before.pre_op_attr_u.attributes.mtime.seconds = inode->mtime / 1000000;
179 wcc->before.pre_op_attr_u.attributes.mtime.nseconds = (inode->mtime % 1000000) * 1000;
180 wcc->before.pre_op_attr_u.attributes.ctime.seconds = inode->ctime / 1000000;
181 wcc->before.pre_op_attr_u.attributes.ctime.nseconds = (inode->ctime % 1000000) * 1000;
184 void nfsproc3_null_3_svc(void *argp, RPCRequest *req)
186 async_rpc_send_reply(req, NULL);
189 void nfsproc3_getattr_3_svc(nfs_fh3 *argp, RPCRequest *req)
192 memset(&result, 0, sizeof(result));
194 BlueSkyInode *inode = lookup_fh(req, argp);
196 result.status = NFS3_OK;
197 g_mutex_lock(inode->lock);
198 encode_fattr3(&result.getattr3res_u.attributes, inode);
199 g_mutex_unlock(inode->lock);
201 result.status = NFS3ERR_STALE;
204 async_rpc_send_reply(req, &result);
207 void nfsproc3_setattr_3_svc(setattr3args *argp, RPCRequest *req)
210 memset(&result, 0, sizeof(result));
212 result.wccstat3_u.wcc.before.present = FALSE;
213 result.wccstat3_u.wcc.after.present = FALSE;
214 BlueSkyInode *inode = lookup_fh(req, &argp->object);
216 result.status = NFS3ERR_STALE;
217 async_rpc_send_reply(req, &result);
221 g_mutex_lock(inode->lock);
222 encode_pre_wcc(&result.wccstat3_u.wcc, inode);
223 if (argp->guard.check) {
224 if (inode->ctime != decode_nfstime3(&argp->guard.sattrguard3_u.ctime)) {
225 result.status = NFS3ERR_NOT_SYNC;
226 result.wccstat3_u.wcc.after.present = TRUE;
227 encode_fattr3(&result.wccstat3_u.wcc.after.post_op_attr_u.attributes, inode);
228 g_mutex_unlock(inode->lock);
229 async_rpc_send_reply(req, &result);
234 set_attributes(inode, &argp->new_attributes);
236 result.wccstat3_u.wcc.after.present = TRUE;
237 encode_fattr3(&result.wccstat3_u.wcc.after.post_op_attr_u.attributes,
239 result.status = NFS3_OK;
241 bluesky_inode_do_sync(inode);
243 g_mutex_unlock(inode->lock);
244 async_rpc_send_reply(req, &result);
247 void nfsproc3_lookup_3_svc(diropargs3 *argp, RPCRequest *req)
250 memset(&result, 0, sizeof(result));
252 BlueSkyInode *dir = lookup_fh(req, &argp->dir);
254 result.status = NFS3ERR_STALE;
255 result.lookup3res_u.resfail.present = FALSE;
256 async_rpc_send_reply(req, &result);
260 g_mutex_lock(dir->lock);
261 result.lookup3res_u.resfail.present = TRUE;
262 encode_fattr3(&result.lookup3res_u.resfail.post_op_attr_u.attributes, dir);
263 if (!validate_filename(argp->name)) {
264 if (strlen(argp->name) > 255)
265 result.status = NFS3ERR_NAMETOOLONG;
267 result.status = NFS3ERR_NOENT;
268 g_mutex_unlock(dir->lock);
269 async_rpc_send_reply(req, &result);
273 /* TODO: Special-case "." and "..". */
274 uint64_t inum = bluesky_directory_lookup(dir, argp->name);
276 result.status = NFS3ERR_NOENT;
277 g_mutex_unlock(dir->lock);
278 async_rpc_send_reply(req, &result);
282 result.lookup3res_u.resok.dir_attributes.present = TRUE;
283 encode_fattr3(&result.lookup3res_u.resok.dir_attributes.post_op_attr_u.attributes, dir);
284 g_mutex_unlock(dir->lock);
286 BlueSkyInode *inode = bluesky_get_inode(fs, inum);
288 result.status = NFS3ERR_NOENT;
289 async_rpc_send_reply(req, &result);
292 g_mutex_lock(inode->lock);
293 schedule_inode_unref(req, inode);
295 result.status = NFS3_OK;
296 result.lookup3res_u.resok.obj_attributes.present = TRUE;
297 encode_fattr3(&result.lookup3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
300 fh_bytes = GUINT64_TO_BE(inum);
301 result.lookup3res_u.resok.object.data.data_len = 8;
302 result.lookup3res_u.resok.object.data.data_val = (char *)&fh_bytes;
304 g_mutex_unlock(inode->lock);
305 async_rpc_send_reply(req, &result);
308 void nfsproc3_access_3_svc(access3args *argp, RPCRequest *req)
311 memset(&result, 0, sizeof(result));
313 BlueSkyInode *inode = lookup_fh(req, &argp->object);
315 result.status = NFS3ERR_STALE;
316 result.access3res_u.resfail.present = FALSE;
317 async_rpc_send_reply(req, &result);
321 g_mutex_lock(inode->lock);
322 result.status = NFS3_OK;
323 result.access3res_u.resok.obj_attributes.present = TRUE;
324 encode_fattr3(&result.access3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
325 result.access3res_u.resok.access = argp->access;
326 g_mutex_unlock(inode->lock);
328 async_rpc_send_reply(req, &result);
331 void nfsproc3_readlink_3_svc(nfs_fh3 *argp, RPCRequest *req)
334 memset(&result, 0, sizeof(result));
336 BlueSkyInode *inode = lookup_fh(req, argp);
338 g_mutex_lock(inode->lock);
339 if (inode->type == BLUESKY_SYMLINK) {
340 result.status = NFS3_OK;
341 result.readlink3res_u.resok.symlink_attributes.present = TRUE;
342 encode_fattr3(&result.readlink3res_u.resok.symlink_attributes.post_op_attr_u.attributes, inode);
343 result.readlink3res_u.resok.data = inode->symlink_contents;
345 result.status = NFS3ERR_INVAL;
346 result.readlink3res_u.resfail.present = TRUE;
347 encode_fattr3(&result.readlink3res_u.resfail.post_op_attr_u.attributes, inode);
349 g_mutex_unlock(inode->lock);
351 result.status = NFS3ERR_STALE;
354 async_rpc_send_reply(req, &result);
357 void nfsproc3_read_3_svc(read3args *argp, RPCRequest *req)
360 memset(&result, 0, sizeof(result));
361 char buf[NFS_MAXSIZE];
363 bluesky_flushd_invoke_conditional(fs);
365 BlueSkyInode *inode = lookup_fh(req, &argp->file);
367 result.status = NFS3ERR_STALE;
368 result.read3res_u.resfail.present = FALSE;
369 async_rpc_send_reply(req, &result);
373 g_mutex_lock(inode->lock);
375 int count = argp->count;
376 if (argp->offset >= inode->size) {
378 result.read3res_u.resok.eof = TRUE;
380 count = MIN(count, NFS_MAXSIZE);
381 count = MIN(count, inode->size - argp->offset);
382 if (argp->offset + count == inode->size)
383 result.read3res_u.resok.eof = TRUE;
385 result.read3res_u.resok.eof = FALSE;
387 bluesky_file_read(inode, argp->offset, buf, count);
390 result.status = NFS3_OK;
391 result.read3res_u.resok.file_attributes.present = TRUE;
392 encode_fattr3(&result.read3res_u.resok.file_attributes.post_op_attr_u.attributes, inode);
393 result.read3res_u.resok.count = count;
394 result.read3res_u.resok.data.data_val = buf;
395 result.read3res_u.resok.data.data_len = count;
397 g_mutex_unlock(inode->lock);
399 async_rpc_send_reply(req, &result);
402 void nfsproc3_write_3_svc(write3args *argp, RPCRequest *req)
405 memset(&result, 0, sizeof(result));
407 memset(&wcc, 0, sizeof(wcc));
409 bluesky_flushd_invoke_conditional(fs);
411 BlueSkyInode *inode = lookup_fh(req, &argp->file);
413 result.status = NFS3ERR_STALE;
414 result.write3res_u.resfail = wcc;
415 async_rpc_send_reply(req, &result);
420 /* FIXME: Hack to throttle writes when there is too much dirty data still
421 * to be written out. */
422 while (g_atomic_int_get(&fs->cache_dirty) > 4096
423 || g_atomic_int_get(&fs->cache_total) > 8192) {
424 g_print("Too many dirty pages (%d) or total pages (%d); throttling writes...\n",
425 g_atomic_int_get(&fs->cache_dirty),
426 g_atomic_int_get(&fs->cache_total));
427 struct timespec delay;
430 nanosleep(&delay, NULL);
434 g_mutex_lock(inode->lock);
436 encode_pre_wcc(&wcc, inode);
437 if (inode->type != BLUESKY_REGULAR) {
438 result.status = NFS3ERR_INVAL;
439 result.write3res_u.resfail = wcc;
440 g_mutex_unlock(inode->lock);
441 async_rpc_send_reply(req, &result);
445 uint64_t lastbyte = argp->offset + argp->count;
446 if (lastbyte > inode->size) {
447 bluesky_file_truncate(inode, lastbyte);
450 if (argp->data.data_len < argp->count) {
453 bluesky_file_write(inode, argp->offset,
454 argp->data.data_val, argp->count);
457 wcc.after.present = TRUE;
458 encode_fattr3(&wcc.after.post_op_attr_u.attributes, inode);
459 result.write3res_u.resok.file_wcc = wcc;
460 result.write3res_u.resok.count = argp->count;
461 result.write3res_u.resok.committed = UNSTABLE;
462 memcpy(result.write3res_u.resok.verf,
463 nfsd_instance_verf_cookie, NFS3_WRITEVERFSIZE);
465 if (argp->stable != UNSTABLE) {
466 bluesky_inode_do_sync(inode);
467 result.write3res_u.resok.committed = FILE_SYNC;
470 g_mutex_unlock(inode->lock);
472 async_rpc_send_reply(req, &result);
475 void nfsproc3_create_3_svc(create3args *argp, RPCRequest *req)
478 memset(&result, 0, sizeof(result));
480 memset(&wcc, 0, sizeof(wcc));
482 BlueSkyInode *dir = lookup_fh(req, &argp->where.dir);
484 result.status = NFS3ERR_STALE;
485 result.diropres3_u.resfail = wcc;
486 async_rpc_send_reply(req, &result);
490 g_mutex_lock(dir->lock);
492 encode_pre_wcc(&wcc, dir);
493 if (dir->type != BLUESKY_DIRECTORY) {
494 result.status = NFS3ERR_NOTDIR;
495 result.diropres3_u.resfail = wcc;
496 g_mutex_unlock(dir->lock);
497 async_rpc_send_reply(req, &result);
501 if (!validate_filename(argp->where.name)
502 || strcmp(argp->where.name, ".") == 0
503 || strcmp(argp->where.name, "..") == 0)
505 result.status = NFS3ERR_EXIST;
506 result.diropres3_u.resfail = wcc;
507 g_mutex_unlock(dir->lock);
508 async_rpc_send_reply(req, &result);
512 g_mutex_lock(fs->lock);
514 file = bluesky_new_inode(bluesky_fs_alloc_inode(fs), fs, BLUESKY_REGULAR);
517 int64_t time = bluesky_get_current_time();
522 g_mutex_lock(file->lock);
523 bluesky_insert_inode(fs, file);
524 g_mutex_unlock(fs->lock);
525 bluesky_directory_insert(dir, argp->where.name, file->inum);
527 bluesky_inode_update_ctime(dir, TRUE);
528 bluesky_inode_update_ctime(file, FALSE);
530 wcc.after.present = TRUE;
531 encode_fattr3(&wcc.after.post_op_attr_u.attributes, dir);
532 result.diropres3_u.resok.obj_attributes.present = TRUE;
533 encode_fattr3(&result.diropres3_u.resok.obj_attributes.post_op_attr_u.attributes, file);
534 result.diropres3_u.resok.dir_wcc = wcc;
537 fh_bytes = GUINT64_TO_BE(file->inum);
538 result.diropres3_u.resok.obj.present = TRUE;
539 result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_len = 8;
540 result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_val = (char *)&fh_bytes;
542 bluesky_inode_do_sync(file);
543 bluesky_inode_do_sync(dir);
544 g_mutex_unlock(file->lock);
545 g_mutex_unlock(dir->lock);
547 async_rpc_send_reply(req, &result);
550 void nfsproc3_mkdir_3_svc(mkdir3args *argp, RPCRequest *req)
553 memset(&result, 0, sizeof(result));
555 memset(&wcc, 0, sizeof(wcc));
557 BlueSkyInode *dir = lookup_fh(req, &argp->where.dir);
559 result.status = NFS3ERR_STALE;
560 result.diropres3_u.resfail = wcc;
561 async_rpc_send_reply(req, &result);
565 g_mutex_lock(dir->lock);
567 encode_pre_wcc(&wcc, dir);
568 if (dir->type != BLUESKY_DIRECTORY) {
569 result.status = NFS3ERR_NOTDIR;
570 result.diropres3_u.resfail = wcc;
571 g_mutex_unlock(dir->lock);
572 async_rpc_send_reply(req, &result);
576 if (!validate_filename(argp->where.name)
577 || strcmp(argp->where.name, ".") == 0
578 || strcmp(argp->where.name, "..") == 0)
580 result.status = NFS3ERR_EXIST;
581 result.diropres3_u.resfail = wcc;
582 g_mutex_unlock(dir->lock);
583 async_rpc_send_reply(req, &result);
587 g_mutex_lock(fs->lock);
589 file = bluesky_new_inode(bluesky_fs_alloc_inode(fs), fs, BLUESKY_DIRECTORY);
592 int64_t time = bluesky_get_current_time();
597 g_mutex_lock(file->lock);
598 bluesky_insert_inode(fs, file);
599 g_mutex_unlock(fs->lock);
600 bluesky_directory_insert(dir, argp->where.name, file->inum);
601 set_attributes(file, &argp->attributes);
603 bluesky_inode_update_ctime(dir, TRUE);
604 bluesky_inode_update_ctime(file, FALSE);
606 wcc.after.present = TRUE;
607 encode_fattr3(&wcc.after.post_op_attr_u.attributes, dir);
608 result.diropres3_u.resok.obj_attributes.present = TRUE;
609 encode_fattr3(&result.diropres3_u.resok.obj_attributes.post_op_attr_u.attributes, file);
610 result.diropres3_u.resok.dir_wcc = wcc;
613 fh_bytes = GUINT64_TO_BE(file->inum);
614 result.diropres3_u.resok.obj.present = TRUE;
615 result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_len = 8;
616 result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_val = (char *)&fh_bytes;
618 bluesky_inode_do_sync(file);
619 bluesky_inode_do_sync(dir);
620 g_mutex_unlock(file->lock);
621 g_mutex_unlock(dir->lock);
622 async_rpc_send_reply(req, &result);
625 void nfsproc3_symlink_3_svc(symlink3args *argp, RPCRequest *req)
628 memset(&result, 0, sizeof(result));
630 memset(&wcc, 0, sizeof(wcc));
632 BlueSkyInode *dir = lookup_fh(req, &argp->where.dir);
634 result.status = NFS3ERR_STALE;
635 result.diropres3_u.resfail = wcc;
636 async_rpc_send_reply(req, &result);
639 g_mutex_lock(dir->lock);
641 encode_pre_wcc(&wcc, dir);
642 if (dir->type != BLUESKY_DIRECTORY) {
643 result.status = NFS3ERR_NOTDIR;
644 result.diropres3_u.resfail = wcc;
645 g_mutex_unlock(dir->lock);
646 async_rpc_send_reply(req, &result);
650 if (!validate_filename(argp->where.name)
651 || strcmp(argp->where.name, ".") == 0
652 || strcmp(argp->where.name, "..") == 0)
654 result.status = NFS3ERR_EXIST;
655 result.diropres3_u.resfail = wcc;
656 g_mutex_unlock(dir->lock);
657 async_rpc_send_reply(req, &result);
661 g_mutex_lock(fs->lock);
663 file = bluesky_new_inode(bluesky_fs_alloc_inode(fs), fs, BLUESKY_SYMLINK);
666 int64_t time = bluesky_get_current_time();
671 file->symlink_contents = g_strdup(argp->symlink.symlink_data);
672 g_mutex_lock(file->lock);
673 bluesky_insert_inode(fs, file);
674 g_mutex_unlock(fs->lock);
675 bluesky_directory_insert(dir, argp->where.name, file->inum);
677 bluesky_inode_update_ctime(dir, TRUE);
678 bluesky_inode_update_ctime(file, FALSE);
680 wcc.after.present = TRUE;
681 encode_fattr3(&wcc.after.post_op_attr_u.attributes, dir);
682 result.diropres3_u.resok.obj_attributes.present = TRUE;
683 encode_fattr3(&result.diropres3_u.resok.obj_attributes.post_op_attr_u.attributes, file);
684 result.diropres3_u.resok.dir_wcc = wcc;
687 fh_bytes = GUINT64_TO_BE(file->inum);
688 result.diropres3_u.resok.obj.present = TRUE;
689 result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_len = 8;
690 result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_val = (char *)&fh_bytes;
692 bluesky_inode_do_sync(file);
693 bluesky_inode_do_sync(dir);
694 g_mutex_unlock(file->lock);
695 g_mutex_unlock(dir->lock);
696 async_rpc_send_reply(req, &result);
699 void nfsproc3_mknod_3_svc(mknod3args *argp, RPCRequest *req)
702 memset(&result, 0, sizeof(result));
704 result.status = NFS3ERR_NOTSUPP;
706 async_rpc_send_reply(req, &result);
709 void nfsproc3_remove_3_svc(diropargs3 *argp, RPCRequest *req)
712 memset(&result, 0, sizeof(result));
714 BlueSkyInode *dir = lookup_fh(req, &argp->dir);
716 result.status = NFS3ERR_STALE;
717 async_rpc_send_reply(req, &result);
721 g_mutex_lock(dir->lock);
723 encode_pre_wcc(&result.wccstat3_u.wcc, dir);
725 if (!validate_filename(argp->name)
726 || strcmp(argp->name, ".") == 0
727 || strcmp(argp->name, "..") == 0)
729 result.status = NFS3ERR_NOENT;
730 g_mutex_unlock(dir->lock);
731 async_rpc_send_reply(req, &result);
735 /* TODO: Decrement link count, deallocate inode if needed. */
737 bluesky_directory_remove(dir, argp->name);
739 result.status = NFS3_OK;
740 result.wccstat3_u.wcc.after.present = TRUE;
741 encode_fattr3(&result.wccstat3_u.wcc.after.post_op_attr_u.attributes,
744 bluesky_inode_do_sync(dir);
745 g_mutex_unlock(dir->lock);
746 async_rpc_send_reply(req, &result);
749 void nfsproc3_rmdir_3_svc(diropargs3 *argp, RPCRequest *req)
752 memset(&result, 0, sizeof(result));
754 BlueSkyInode *dir = lookup_fh(req, &argp->dir);
756 result.status = NFS3ERR_STALE;
757 async_rpc_send_reply(req, &result);
761 g_mutex_lock(dir->lock);
763 encode_pre_wcc(&result.wccstat3_u.wcc, dir);
765 if (!validate_filename(argp->name)
766 || strcmp(argp->name, ".") == 0
767 || strcmp(argp->name, "..") == 0)
769 result.status = NFS3ERR_NOENT;
770 g_mutex_unlock(dir->lock);
771 async_rpc_send_reply(req, &result);
775 uint64_t inum = bluesky_directory_lookup(dir, argp->name);
776 BlueSkyInode *inode = bluesky_get_inode(fs, inum);
778 result.status = NFS3ERR_NOENT;
779 g_mutex_unlock(dir->lock);
780 async_rpc_send_reply(req, &result);
783 g_mutex_lock(inode->lock);
784 schedule_inode_unref(req, inode);
786 if (inode->type != BLUESKY_DIRECTORY) {
787 result.status = NFS3ERR_NOTDIR;
788 g_mutex_unlock(inode->lock);
789 g_mutex_unlock(dir->lock);
790 async_rpc_send_reply(req, &result);
793 if (g_sequence_get_length(inode->dirents) > 0) {
794 printf("Directory not empty: %d entries\n",
795 g_sequence_get_length(inode->dirents));
796 result.status = NFS3ERR_NOTEMPTY;
797 g_mutex_unlock(inode->lock);
798 g_mutex_unlock(dir->lock);
799 async_rpc_send_reply(req, &result);
803 /* TODO: Decrement link count, deallocate inode if needed. */
805 bluesky_directory_remove(dir, argp->name);
807 result.status = NFS3_OK;
808 result.wccstat3_u.wcc.after.present = TRUE;
809 encode_fattr3(&result.wccstat3_u.wcc.after.post_op_attr_u.attributes,
812 bluesky_inode_do_sync(dir);
813 bluesky_inode_do_sync(inode);
814 g_mutex_unlock(inode->lock);
815 g_mutex_unlock(dir->lock);
816 async_rpc_send_reply(req, &result);
819 void nfsproc3_rename_3_svc(rename3args *argp, RPCRequest *req)
822 memset(&result, 0, sizeof(result));
823 wcc_data *wcc1 = &result.rename3res_u.res.fromdir_wcc;
824 wcc_data *wcc2 = &result.rename3res_u.res.todir_wcc;
826 BlueSkyInode *dir1 = lookup_fh(req, &argp->from.dir);
828 result.status = NFS3ERR_STALE;
829 async_rpc_send_reply(req, &result);
833 BlueSkyInode *dir2 = lookup_fh(req, &argp->to.dir);
835 result.status = NFS3ERR_STALE;
836 async_rpc_send_reply(req, &result);
840 if (dir1->inum < dir2->inum) {
841 g_mutex_lock(dir1->lock);
842 g_mutex_lock(dir2->lock);
843 } else if (dir1->inum > dir2->inum) {
844 g_mutex_lock(dir2->lock);
845 g_mutex_lock(dir1->lock);
847 encode_pre_wcc(wcc1, dir1);
848 encode_pre_wcc(wcc2, dir1);
850 gboolean status = bluesky_rename(dir1, argp->from.name,
854 wcc1->after.present = TRUE;
855 encode_fattr3(&wcc1->after.post_op_attr_u.attributes, dir1);
856 wcc2->after.present = TRUE;
857 encode_fattr3(&wcc2->after.post_op_attr_u.attributes, dir2);
859 result.status = NFS3_OK;
861 result.status = NFS3ERR_PERM;
863 bluesky_inode_do_sync(dir2);
864 bluesky_inode_do_sync(dir1);
866 g_mutex_unlock(dir1->lock);
867 if (dir1->inum != dir2->inum)
868 g_mutex_unlock(dir2->lock);
869 async_rpc_send_reply(req, &result);
872 void nfsproc3_link_3_svc(link3args *argp, RPCRequest *req)
875 memset(&result, 0, sizeof(result));
877 memset(&wcc, 0, sizeof(wcc));
879 BlueSkyInode *inode = lookup_fh(req, &argp->file);
881 result.status = NFS3ERR_STALE;
882 result.link3res_u.res.linkdir_wcc = wcc;
883 async_rpc_send_reply(req, &result);
886 g_mutex_lock(inode->lock);
888 BlueSkyInode *dir = lookup_fh(req, &argp->link.dir);
890 result.status = NFS3ERR_STALE;
891 result.link3res_u.res.linkdir_wcc = wcc;
892 g_mutex_unlock(inode->lock);
893 async_rpc_send_reply(req, &result);
896 g_mutex_lock(dir->lock);
898 encode_pre_wcc(&wcc, dir);
899 if (dir->type != BLUESKY_DIRECTORY) {
900 result.status = NFS3ERR_NOTDIR;
901 result.link3res_u.res.linkdir_wcc = wcc;
902 g_mutex_unlock(inode->lock);
903 g_mutex_unlock(dir->lock);
904 async_rpc_send_reply(req, &result);
908 if (!validate_filename(argp->link.name)
909 || strcmp(argp->link.name, ".") == 0
910 || strcmp(argp->link.name, "..") == 0
911 || bluesky_directory_lookup(dir, argp->link.name) != 0)
913 result.status = NFS3ERR_EXIST;
914 result.link3res_u.res.linkdir_wcc = wcc;
915 g_mutex_unlock(inode->lock);
916 g_mutex_unlock(dir->lock);
917 async_rpc_send_reply(req, &result);
921 if (!bluesky_directory_insert(dir, argp->link.name, inode->inum)) {
922 result.status = NFS3ERR_EXIST;
923 result.link3res_u.res.linkdir_wcc = wcc;
924 g_mutex_unlock(inode->lock);
925 g_mutex_unlock(dir->lock);
926 async_rpc_send_reply(req, &result);
930 bluesky_inode_update_ctime(inode, FALSE);
931 bluesky_inode_update_ctime(dir, TRUE);
933 result.status = NFS3_OK;
934 wcc.after.present = TRUE;
935 encode_fattr3(&wcc.after.post_op_attr_u.attributes, dir);
936 result.link3res_u.res.file_attributes.present = TRUE;
937 encode_fattr3(&result.link3res_u.res.file_attributes.post_op_attr_u.attributes, inode);
938 result.link3res_u.res.linkdir_wcc = wcc;
940 bluesky_inode_do_sync(inode);
941 bluesky_inode_do_sync(dir);
942 g_mutex_unlock(inode->lock);
943 g_mutex_unlock(dir->lock);
944 async_rpc_send_reply(req, &result);
947 gint bluesky_dirent_compare(gconstpointer a, gconstpointer b,
950 #define MAX_READDIR_DIRENTS 64
951 void nfsproc3_readdir_3_svc(readdir3args *argp, RPCRequest *req)
954 memset(&result, 0, sizeof(result));
956 BlueSkyInode *dir = lookup_fh(req, &argp->dir);
958 result.status = NFS3ERR_STALE;
959 result.readdir3res_u.resfail.present = FALSE;
960 async_rpc_send_reply(req, &result);
963 g_mutex_lock(dir->lock);
965 result.status = NFS3_OK;
966 result.readdir3res_u.resok.dir_attributes.present = TRUE;
967 encode_fattr3(&result.readdir3res_u.resok.dir_attributes.post_op_attr_u.attributes, dir);
968 memset(result.readdir3res_u.resok.cookieverf, 0,
969 sizeof(result.readdir3res_u.resok.cookieverf));
971 entry3 dirents[MAX_READDIR_DIRENTS];
974 BlueSkyDirent start = {NULL, NULL, argp->cookie, 0};
975 GSequenceIter *i = g_sequence_search(dir->dirents, &start,
976 bluesky_dirent_compare, NULL);
978 while (count < MAX_READDIR_DIRENTS && !g_sequence_iter_is_end(i)) {
979 BlueSkyDirent *d = g_sequence_get(i);
980 dirents[count].fileid = d->inum;
981 dirents[count].name = d->name;
982 dirents[count].cookie = d->cookie;
983 dirents[count].nextentry = NULL;
985 dirents[count - 1].nextentry = &dirents[count];
986 i = g_sequence_iter_next(i);
991 result.readdir3res_u.resok.reply.entries = &dirents[0];
993 result.readdir3res_u.resok.reply.entries = NULL;
994 result.readdir3res_u.resok.reply.eof = g_sequence_iter_is_end(i);
996 g_mutex_unlock(dir->lock);
997 async_rpc_send_reply(req, &result);
1000 void nfsproc3_readdirplus_3_svc(readdirplus3args *argp, RPCRequest *req)
1002 /* XDR-encoded sizes:
1003 * post_op_attr: 88 bytes
1004 * base readdirplus3resok: 88 + 16 bytes
1005 * base directory entry: 24 bytes + filename
1006 * attributes/fh3: 88 + 8 + filehandle size
1008 size_t dircount = 88 + 16, attrcount = 0;
1009 readdirplus3res result;
1010 memset(&result, 0, sizeof(result));
1012 BlueSkyInode *dir = lookup_fh(req, &argp->dir);
1014 result.status = NFS3ERR_STALE;
1015 result.readdirplus3res_u.resfail.present = FALSE;
1016 async_rpc_send_reply(req, &result);
1019 g_mutex_lock(dir->lock);
1021 result.status = NFS3_OK;
1022 result.readdirplus3res_u.resok.dir_attributes.present = TRUE;
1023 encode_fattr3(&result.readdirplus3res_u.resok.dir_attributes.post_op_attr_u.attributes, dir);
1024 memset(result.readdirplus3res_u.resok.cookieverf, 0,
1025 sizeof(result.readdirplus3res_u.resok.cookieverf));
1027 entryplus3 dirents[MAX_READDIR_DIRENTS];
1028 uint64_t fh_bytes[MAX_READDIR_DIRENTS];
1032 BlueSkyDirent start = {NULL, NULL, argp->cookie, 0};
1034 /* Perform a prefetch pass on inodes: for all the inodes we think we will
1035 * return information about, try to load each one but don't wait. This
1036 * should let multiple inodes be fetched in parallel, instead of
1037 * sequentially in the loop that follows. */
1038 i = g_sequence_search(dir->dirents, &start, bluesky_dirent_compare, NULL);
1039 while (count < MAX_READDIR_DIRENTS
1040 && !g_sequence_iter_is_end(i)
1041 && dircount <= argp->dircount
1042 && dircount + attrcount <= argp->maxcount)
1044 BlueSkyDirent *d = g_sequence_get(i);
1045 bluesky_inode_prefetch(fs, d->inum);
1046 dircount += 24 + ((strlen(d->name) + 3) & ~3);
1047 attrcount += 88 + 8 + 8;
1048 i = g_sequence_iter_next(i);
1051 i = g_sequence_search(dir->dirents, &start, bluesky_dirent_compare, NULL);
1055 while (count < MAX_READDIR_DIRENTS && !g_sequence_iter_is_end(i)) {
1056 BlueSkyDirent *d = g_sequence_get(i);
1057 BlueSkyInode *inode = bluesky_get_inode(fs, d->inum);
1058 if (inode != NULL) {
1059 g_mutex_lock(inode->lock);
1060 dircount += 24 + ((strlen(d->name) + 3) & ~3);
1061 attrcount += 88 + 8 + 8;
1062 if (dircount > argp->dircount
1063 || dircount + attrcount > argp->maxcount)
1065 g_mutex_unlock(inode->lock);
1066 bluesky_inode_unref(inode);
1069 dirents[count].fileid = d->inum;
1070 dirents[count].name = d->name;
1071 dirents[count].cookie = d->cookie;
1072 dirents[count].nextentry = NULL;
1073 dirents[count].name_attributes.present = TRUE;
1074 encode_fattr3(&dirents[count].name_attributes.post_op_attr_u.attributes, inode);
1075 fh_bytes[count] = GUINT64_TO_BE(d->inum);
1076 dirents[count].name_handle.present = TRUE;
1077 dirents[count].name_handle.post_op_fh3_u.handle.data.data_len = 8;
1078 dirents[count].name_handle.post_op_fh3_u.handle.data.data_val
1079 = (char *)&fh_bytes[count];
1081 dirents[count - 1].nextentry = &dirents[count];
1083 g_mutex_unlock(inode->lock);
1084 bluesky_inode_unref(inode);
1086 i = g_sequence_iter_next(i);
1090 result.readdirplus3res_u.resok.reply.entries = &dirents[0];
1092 result.readdirplus3res_u.resok.reply.entries = NULL;
1093 result.readdirplus3res_u.resok.reply.eof = g_sequence_iter_is_end(i);
1095 g_mutex_unlock(dir->lock);
1096 async_rpc_send_reply(req, &result);
1099 void nfsproc3_fsstat_3_svc(nfs_fh3 *argp, RPCRequest *req)
1102 memset(&result, 0, sizeof(result));
1104 BlueSkyInode *inode = lookup_fh(req, argp);
1105 if (inode == NULL) {
1106 result.status = NFS3ERR_STALE;
1107 result.fsstat3res_u.resfail.present = FALSE;
1108 async_rpc_send_reply(req, &result);
1111 g_mutex_lock(inode->lock);
1113 result.status = NFS3_OK;
1114 result.fsstat3res_u.resok.obj_attributes.present = TRUE;
1115 encode_fattr3(&result.fsstat3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
1117 result.fsstat3res_u.resok.tbytes = (1 << 30);
1118 result.fsstat3res_u.resok.fbytes = (1 << 30);
1119 result.fsstat3res_u.resok.abytes = (1 << 30);
1120 result.fsstat3res_u.resok.tfiles = 0;
1121 result.fsstat3res_u.resok.ffiles = 0;
1122 result.fsstat3res_u.resok.afiles = 0;
1123 result.fsstat3res_u.resok.invarsec = 0;
1125 g_mutex_unlock(inode->lock);
1126 async_rpc_send_reply(req, &result);
1129 void nfsproc3_fsinfo_3_svc(nfs_fh3 *argp, RPCRequest *req)
1132 memset(&result, 0, sizeof(result));
1134 BlueSkyInode *inode = bluesky_get_inode(fs, 1);
1135 g_mutex_lock(inode->lock);
1136 result.status = NFS3_OK;
1137 result.fsinfo3res_u.resok.obj_attributes.present = TRUE;
1138 encode_fattr3(&result.fsinfo3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
1139 result.fsinfo3res_u.resok.rtmax = NFS_MAXSIZE;
1140 result.fsinfo3res_u.resok.rtpref = NFS_MAXSIZE;
1141 result.fsinfo3res_u.resok.rtmult = NFS_BLOCKSIZE;
1142 result.fsinfo3res_u.resok.wtmax = NFS_MAXSIZE;
1143 result.fsinfo3res_u.resok.wtpref = NFS_MAXSIZE;
1144 result.fsinfo3res_u.resok.wtmult = NFS_BLOCKSIZE;
1145 result.fsinfo3res_u.resok.dtpref = NFS_BLOCKSIZE;
1146 result.fsinfo3res_u.resok.maxfilesize = 0x7fffffffffffffffULL;
1147 result.fsinfo3res_u.resok.time_delta.seconds = 0;
1148 result.fsinfo3res_u.resok.time_delta.nseconds = 1000;
1149 result.fsinfo3res_u.resok.properties
1150 = FSF3_LINK | FSF3_SYMLINK | FSF3_HOMOGENEOUS | FSF3_CANSETTIME;
1152 g_mutex_unlock(inode->lock);
1153 bluesky_inode_unref(inode);
1154 async_rpc_send_reply(req, &result);
1157 void nfsproc3_pathconf_3_svc(nfs_fh3 *argp, RPCRequest *req)
1159 pathconf3res result;
1160 memset(&result, 0, sizeof(result));
1162 BlueSkyInode *inode = bluesky_get_inode(fs, 1);
1163 g_mutex_lock(inode->lock);
1164 result.status = NFS3_OK;
1165 result.pathconf3res_u.resok.obj_attributes.present = TRUE;
1166 encode_fattr3(&result.pathconf3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
1167 result.pathconf3res_u.resok.linkmax = 0xffffffff;
1168 result.pathconf3res_u.resok.name_max = 255;
1169 result.pathconf3res_u.resok.no_trunc = TRUE;
1170 result.pathconf3res_u.resok.chown_restricted = TRUE;
1171 result.pathconf3res_u.resok.case_insensitive = FALSE;
1172 result.pathconf3res_u.resok.case_preserving = TRUE;
1174 g_mutex_unlock(inode->lock);
1175 bluesky_inode_unref(inode);
1176 async_rpc_send_reply(req, &result);
1179 void nfsproc3_commit_3_svc(commit3args *argp, RPCRequest *req)
1182 memset(&result, 0, sizeof(result));
1184 result.status = NFS3_OK;
1186 BlueSkyInode *inode = lookup_fh(req, &argp->file);
1187 if (inode == NULL) {
1188 result.status = NFS3ERR_STALE;
1189 async_rpc_send_reply(req, &result);
1193 g_mutex_lock(inode->lock);
1194 encode_pre_wcc(&result.commit3res_u.resok.file_wcc, inode);
1196 bluesky_inode_do_sync(inode);
1198 result.commit3res_u.resok.file_wcc.after.present = TRUE;
1199 encode_fattr3(&result.commit3res_u.resok.file_wcc.after.post_op_attr_u.attributes, inode);
1200 memcpy(result.commit3res_u.resok.verf,
1201 nfsd_instance_verf_cookie, NFS3_WRITEVERFSIZE);
1203 g_mutex_unlock(inode->lock);
1205 async_rpc_send_reply(req, &result);