Support for sync operations on an inode.
[bluesky.git] / nfs3 / nfs3.c
1 /*
2  * This is sample code generated by rpcgen.
3  * These are only templates and you can use them
4  * as a guideline for developing your own functions.
5  */
6
7 #include "nfs3_prot.h"
8 #include "bluesky.h"
9
10 extern BlueSkyFS *fs;
11
12 /* Check that a string is a valid file name.  We require that it be valid
13  * UTF-8, that it not be empty, and that it not contain embedded forward
14  * slashes.  Also checks that the length of the string is not more than the
15  * maximum allowed length.  This function does allow the names "." and "..".
16  * Returns TRUE if the string is allowed as a filename. */
17 gboolean validate_filename(const char *filename)
18 {
19     if (filename == NULL || filename[0] == '\0')
20         return FALSE;
21     if (strlen(filename) > 255)
22         return FALSE;
23     if (!g_utf8_validate(filename, -1, NULL))
24         return FALSE;
25     if (strchr(filename, '/') != NULL)
26         return FALSE;
27     return TRUE;
28 }
29
30 /* Arrange for a reference to an inode to be dropped when the RPC request
31  * completes. */
32 void schedule_inode_unref(RPCRequest *req, BlueSkyInode *inode)
33 {
34     struct cleanup_list *c = g_new(struct cleanup_list, 1);
35     c->func = (void (*)(void *))bluesky_inode_unref;
36     c->arg = inode;
37     c->next = req->cleanup;
38     req->cleanup = c;
39 }
40
41 /* Look up a BlueSkyInode given an NFS filehandle.  Returns NULL if the
42  * filehandle is invalid. */
43 BlueSkyInode *lookup_fh(RPCRequest *req, nfs_fh3 *fh)
44 {
45     BlueSkyInode *inode = NULL;
46     if (fh->data.data_len == 8) {
47         uint64_t inum = GUINT64_FROM_BE(*(uint64_t *)(fh->data.data_val));
48         inode = bluesky_get_inode(fs, inum);
49         if (inode != NULL)
50             schedule_inode_unref(req, inode);
51     }
52     return inode;
53 }
54
55 int64_t decode_nfstime3(nfstime3 *time)
56 {
57     int64_t result = (int64_t)time->seconds * 1000000;
58     result += time->nseconds / 1000;
59     return result;
60 }
61
62 void set_attributes(BlueSkyInode *inode, sattr3 *attributes)
63 {
64     int64_t now = bluesky_get_current_time();
65
66     if (attributes->mode.set) {
67         inode->mode = attributes->mode.set_uint32_u.val;
68     }
69
70     if (attributes->uid.set) {
71         inode->uid = attributes->uid.set_uint32_u.val;
72     }
73
74     if (attributes->gid.set) {
75         inode->gid = attributes->gid.set_uint32_u.val;
76     }
77
78     if (attributes->size.set) {
79         if (inode->type == BLUESKY_REGULAR) {
80             bluesky_file_truncate(inode, attributes->size.set_uint64_u.val);
81             inode->mtime = now;
82         }
83     }
84
85     switch (attributes->atime.set) {
86     case DONT_CHANGE:
87         break;
88     case SET_TO_SERVER_TIME:
89         inode->atime = now;
90         break;
91     case SET_TO_CLIENT_TIME:
92         inode->atime = decode_nfstime3(&attributes->atime.set_time_u.time);
93         break;
94     }
95
96     switch (attributes->mtime.set) {
97     case DONT_CHANGE:
98         break;
99     case SET_TO_SERVER_TIME:
100         inode->mtime = now;
101         break;
102     case SET_TO_CLIENT_TIME:
103         inode->mtime = decode_nfstime3(&attributes->mtime.set_time_u.time);
104         break;
105     }
106
107     bluesky_inode_update_ctime(inode, FALSE);
108 }
109
110 /* Copy inode attributes into NFS response.  The BlueSkyInode should be locked
111  * by the caller. */
112 void encode_fattr3(struct fattr3 *result, BlueSkyInode *inode)
113 {
114     result->type = inode->type;
115     result->mode = inode->mode;
116     result->nlink = inode->nlink;
117     result->uid = inode->uid;
118     result->gid = inode->gid;
119     result->size = inode->size;
120     result->used = 0;
121     result->rdev.major = 0;
122     result->rdev.minor = 0;
123     result->fsid = 0;
124     result->fileid = inode->inum;
125     result->atime.seconds = inode->atime / 1000000;
126     result->atime.nseconds = (inode->atime % 1000000) * 1000;
127     result->mtime.seconds = inode->mtime / 1000000;
128     result->mtime.nseconds = (inode->mtime % 1000000) * 1000;
129     result->ctime.seconds = inode->ctime / 1000000;
130     result->ctime.nseconds = (inode->ctime % 1000000) * 1000;
131
132     switch (inode->type) {
133     case BLUESKY_SYMLINK:
134         result->size = strlen(inode->symlink_contents);
135         break;
136     default:
137         break;
138     }
139 }
140
141 void encode_pre_wcc(struct wcc_data *wcc, BlueSkyInode *inode)
142 {
143     wcc->before.present = TRUE;
144     wcc->before.pre_op_attr_u.attributes.size = inode->size;
145     wcc->before.pre_op_attr_u.attributes.mtime.seconds = inode->mtime / 1000000;
146     wcc->before.pre_op_attr_u.attributes.mtime.nseconds = (inode->mtime % 1000000) * 1000;
147     wcc->before.pre_op_attr_u.attributes.ctime.seconds = inode->ctime / 1000000;
148     wcc->before.pre_op_attr_u.attributes.ctime.nseconds = (inode->ctime % 1000000) * 1000;
149 }
150
151 void nfsproc3_null_3_svc(void *argp, RPCRequest *req)
152 {
153     async_rpc_send_reply(req, NULL);
154 }
155
156 void nfsproc3_getattr_3_svc(nfs_fh3 *argp, RPCRequest *req)
157 {
158     getattr3res result;
159     memset(&result, 0, sizeof(result));
160
161     BlueSkyInode *inode = lookup_fh(req, argp);
162     if (inode != NULL) {
163         result.status = NFS3_OK;
164         g_mutex_lock(inode->lock);
165         encode_fattr3(&result.getattr3res_u.attributes, inode);
166         g_mutex_unlock(inode->lock);
167     } else {
168         result.status = NFS3ERR_STALE;
169     }
170
171     async_rpc_send_reply(req, &result);
172 }
173
174 void nfsproc3_setattr_3_svc(setattr3args *argp, RPCRequest *req)
175 {
176     wccstat3 result;
177     memset(&result, 0, sizeof(result));
178
179     result.wccstat3_u.wcc.before.present = FALSE;
180     result.wccstat3_u.wcc.after.present = FALSE;
181     BlueSkyInode *inode = lookup_fh(req, &argp->object);
182     if (inode == NULL) {
183         result.status = NFS3ERR_STALE;
184         async_rpc_send_reply(req, &result);
185         return;
186     }
187
188     g_mutex_lock(inode->lock);
189     encode_pre_wcc(&result.wccstat3_u.wcc, inode);
190     if (argp->guard.check) {
191         if (inode->ctime != decode_nfstime3(&argp->guard.sattrguard3_u.ctime)) {
192             result.status = NFS3ERR_NOT_SYNC;
193             result.wccstat3_u.wcc.after.present = TRUE;
194             encode_fattr3(&result.wccstat3_u.wcc.after.post_op_attr_u.attributes, inode);
195             g_mutex_unlock(inode->lock);
196             async_rpc_send_reply(req, &result);
197             return;
198         }
199     }
200
201     set_attributes(inode, &argp->new_attributes);
202
203     result.wccstat3_u.wcc.after.present = TRUE;
204     encode_fattr3(&result.wccstat3_u.wcc.after.post_op_attr_u.attributes,
205                   inode);
206     result.status = NFS3_OK;
207
208     g_mutex_unlock(inode->lock);
209     async_rpc_send_reply(req, &result);
210 }
211
212 void nfsproc3_lookup_3_svc(diropargs3 *argp, RPCRequest *req)
213 {
214     lookup3res result;
215     memset(&result, 0, sizeof(result));
216
217     BlueSkyInode *dir = lookup_fh(req, &argp->dir);
218     if (dir == NULL) {
219         result.status = NFS3ERR_STALE;
220         result.lookup3res_u.resfail.present = FALSE;
221         async_rpc_send_reply(req, &result);
222         return;
223     }
224
225     g_mutex_lock(dir->lock);
226     result.lookup3res_u.resfail.present = TRUE;
227     encode_fattr3(&result.lookup3res_u.resfail.post_op_attr_u.attributes, dir);
228     if (!validate_filename(argp->name)) {
229         if (strlen(argp->name) > 255)
230             result.status = NFS3ERR_NAMETOOLONG;
231         else
232             result.status = NFS3ERR_NOENT;
233         g_mutex_unlock(dir->lock);
234         async_rpc_send_reply(req, &result);
235         return;
236     }
237
238     /* TODO: Special-case "." and "..". */
239     uint64_t inum = bluesky_directory_lookup(dir, argp->name);
240     if (inum == 0) {
241         result.status = NFS3ERR_NOENT;
242         g_mutex_unlock(dir->lock);
243         async_rpc_send_reply(req, &result);
244         return;
245     }
246     BlueSkyInode *inode = bluesky_get_inode(fs, inum);
247     if (inode == NULL) {
248         result.status = NFS3ERR_NOENT;
249         g_mutex_unlock(dir->lock);
250         async_rpc_send_reply(req, &result);
251         return;
252     }
253     g_mutex_lock(inode->lock);
254     schedule_inode_unref(req, inode);
255
256     result.status = NFS3_OK;
257     result.lookup3res_u.resok.dir_attributes.present = TRUE;
258     encode_fattr3(&result.lookup3res_u.resok.dir_attributes.post_op_attr_u.attributes, dir);
259     result.lookup3res_u.resok.obj_attributes.present = TRUE;
260     encode_fattr3(&result.lookup3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
261
262     uint64_t fh_bytes;
263     fh_bytes = GUINT64_TO_BE(inum);
264     result.lookup3res_u.resok.object.data.data_len = 8;
265     result.lookup3res_u.resok.object.data.data_val = (char *)&fh_bytes;
266
267     g_mutex_unlock(inode->lock);
268     g_mutex_unlock(dir->lock);
269     async_rpc_send_reply(req, &result);
270 }
271
272 void nfsproc3_access_3_svc(access3args *argp, RPCRequest *req)
273 {
274     access3res result;
275     memset(&result, 0, sizeof(result));
276
277     BlueSkyInode *inode = lookup_fh(req, &argp->object);
278     if (inode == NULL) {
279         result.status = NFS3ERR_STALE;
280         result.access3res_u.resfail.present = FALSE;
281         async_rpc_send_reply(req, &result);
282         return;
283     }
284
285     g_mutex_lock(inode->lock);
286     result.status = NFS3_OK;
287     result.access3res_u.resok.obj_attributes.present = TRUE;
288     encode_fattr3(&result.access3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
289     result.access3res_u.resok.access = argp->access;
290     g_mutex_unlock(inode->lock);
291
292     async_rpc_send_reply(req, &result);
293 }
294
295 void nfsproc3_readlink_3_svc(nfs_fh3 *argp, RPCRequest *req)
296 {
297     readlink3res result;
298     memset(&result, 0, sizeof(result));
299
300     BlueSkyInode *inode = lookup_fh(req, argp);
301     if (inode != NULL) {
302         g_mutex_lock(inode->lock);
303         if (inode->type == BLUESKY_SYMLINK) {
304             result.status = NFS3_OK;
305             result.readlink3res_u.resok.symlink_attributes.present = TRUE;
306             encode_fattr3(&result.readlink3res_u.resok.symlink_attributes.post_op_attr_u.attributes, inode);
307             result.readlink3res_u.resok.data = inode->symlink_contents;
308         } else {
309             result.status = NFS3ERR_INVAL;
310             result.readlink3res_u.resfail.present = TRUE;
311             encode_fattr3(&result.readlink3res_u.resfail.post_op_attr_u.attributes, inode);
312         }
313         g_mutex_unlock(inode->lock);
314     } else {
315         result.status = NFS3ERR_STALE;
316     }
317
318     async_rpc_send_reply(req, &result);
319 }
320
321 void nfsproc3_read_3_svc(read3args *argp, RPCRequest *req)
322 {
323     read3res result;
324     memset(&result, 0, sizeof(result));
325     char buf[32768];
326
327     BlueSkyInode *inode = lookup_fh(req, &argp->file);
328     if (inode == NULL) {
329         result.status = NFS3ERR_STALE;
330         result.read3res_u.resfail.present = FALSE;
331         async_rpc_send_reply(req, &result);
332         return;
333     }
334
335     g_mutex_lock(inode->lock);
336
337     int count = argp->count;
338     if (argp->offset >= inode->size) {
339         count = 0;
340         result.read3res_u.resok.eof = TRUE;
341     } else {
342         count = MIN(count, inode->size - argp->offset);
343         if (argp->offset + count == inode->size)
344             result.read3res_u.resok.eof = TRUE;
345         else
346             result.read3res_u.resok.eof = FALSE;
347
348         bluesky_file_read(inode, argp->offset, buf, count);
349     }
350
351     result.status = NFS3_OK;
352     result.read3res_u.resok.file_attributes.present = TRUE;
353     encode_fattr3(&result.read3res_u.resok.file_attributes.post_op_attr_u.attributes, inode);
354     result.read3res_u.resok.count = count;
355     result.read3res_u.resok.data.data_val = buf;
356     result.read3res_u.resok.data.data_len = count;
357
358     g_mutex_unlock(inode->lock);
359
360     async_rpc_send_reply(req, &result);
361 }
362
363 void nfsproc3_write_3_svc(write3args *argp, RPCRequest *req)
364 {
365     write3res result;
366     memset(&result, 0, sizeof(result));
367     struct wcc_data wcc;
368     memset(&wcc, 0, sizeof(wcc));
369
370     BlueSkyInode *inode = lookup_fh(req, &argp->file);
371     if (inode == NULL) {
372         result.status = NFS3ERR_STALE;
373         result.write3res_u.resfail = wcc;
374         async_rpc_send_reply(req, &result);
375         return;
376     }
377
378     g_mutex_lock(inode->lock);
379
380     encode_pre_wcc(&wcc, inode);
381     if (inode->type != BLUESKY_REGULAR) {
382         result.status = NFS3ERR_INVAL;
383         result.write3res_u.resfail = wcc;
384         g_mutex_unlock(inode->lock);
385         async_rpc_send_reply(req, &result);
386         return;
387     }
388
389     uint64_t lastbyte = argp->offset + argp->count;
390     if (lastbyte > inode->size) {
391         bluesky_file_truncate(inode, lastbyte);
392     }
393
394     if (argp->data.data_len < argp->count) {
395         /* ??? */
396     } else {
397         bluesky_file_write(inode, argp->offset,
398                            argp->data.data_val, argp->count);
399     }
400
401     wcc.after.present = TRUE;
402     encode_fattr3(&wcc.after.post_op_attr_u.attributes, inode);
403     result.write3res_u.resok.file_wcc = wcc;
404     result.write3res_u.resok.count = argp->count;
405     result.write3res_u.resok.committed = FILE_SYNC;
406
407     g_mutex_unlock(inode->lock);
408
409     async_rpc_send_reply(req, &result);
410 }
411
412 void nfsproc3_create_3_svc(create3args *argp, RPCRequest *req)
413 {
414     diropres3 result;
415     memset(&result, 0, sizeof(result));
416     struct wcc_data wcc;
417     memset(&wcc, 0, sizeof(wcc));
418
419     BlueSkyInode *dir = lookup_fh(req, &argp->where.dir);
420     if (dir == NULL) {
421         result.status = NFS3ERR_STALE;
422         result.diropres3_u.resfail = wcc;
423         async_rpc_send_reply(req, &result);
424         return;
425     }
426
427     g_mutex_lock(dir->lock);
428
429     encode_pre_wcc(&wcc, dir);
430     if (dir->type != BLUESKY_DIRECTORY) {
431         result.status = NFS3ERR_NOTDIR;
432         result.diropres3_u.resfail = wcc;
433         g_mutex_unlock(dir->lock);
434         async_rpc_send_reply(req, &result);
435         return;
436     }
437
438     if (!validate_filename(argp->where.name)
439         || strcmp(argp->where.name, ".") == 0
440         || strcmp(argp->where.name, "..") == 0)
441     {
442         result.status = NFS3ERR_EXIST;
443         result.diropres3_u.resfail = wcc;
444         g_mutex_unlock(dir->lock);
445         async_rpc_send_reply(req, &result);
446         return;
447     }
448
449     BlueSkyInode *file;
450     file = bluesky_new_inode(bluesky_fs_alloc_inode(fs), fs, BLUESKY_REGULAR);
451     file->nlink = 1;
452     file->mode = 0755;
453     int64_t time = bluesky_get_current_time();
454     printf("time: %"PRIi64"\n", time);
455     file->mtime = time;
456     file->ctime = time;
457     file->atime = time;
458     file->ntime = time;
459     g_mutex_lock(file->lock);
460     bluesky_insert_inode(fs, file);
461     bluesky_directory_insert(dir, argp->where.name, file->inum);
462
463     bluesky_inode_update_ctime(dir, TRUE);
464
465     wcc.after.present = TRUE;
466     encode_fattr3(&wcc.after.post_op_attr_u.attributes, dir);
467     result.diropres3_u.resok.obj_attributes.present = TRUE;
468     encode_fattr3(&result.diropres3_u.resok.obj_attributes.post_op_attr_u.attributes, file);
469     result.diropres3_u.resok.dir_wcc = wcc;
470
471     uint64_t fh_bytes;
472     fh_bytes = GUINT64_TO_BE(file->inum);
473     result.diropres3_u.resok.obj.present = TRUE;
474     result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_len = 8;
475     result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_val = (char *)&fh_bytes;
476
477     g_mutex_unlock(file->lock);
478     g_mutex_unlock(dir->lock);
479
480     async_rpc_send_reply(req, &result);
481 }
482
483 void nfsproc3_mkdir_3_svc(mkdir3args *argp, RPCRequest *req)
484 {
485     diropres3 result;
486     memset(&result, 0, sizeof(result));
487     struct wcc_data wcc;
488     memset(&wcc, 0, sizeof(wcc));
489
490     BlueSkyInode *dir = lookup_fh(req, &argp->where.dir);
491     if (dir == NULL) {
492         result.status = NFS3ERR_STALE;
493         result.diropres3_u.resfail = wcc;
494         async_rpc_send_reply(req, &result);
495         return;
496     }
497
498     g_mutex_lock(dir->lock);
499
500     encode_pre_wcc(&wcc, dir);
501     if (dir->type != BLUESKY_DIRECTORY) {
502         result.status = NFS3ERR_NOTDIR;
503         result.diropres3_u.resfail = wcc;
504         g_mutex_unlock(dir->lock);
505         async_rpc_send_reply(req, &result);
506         return;
507     }
508
509     if (!validate_filename(argp->where.name)
510         || strcmp(argp->where.name, ".") == 0
511         || strcmp(argp->where.name, "..") == 0)
512     {
513         result.status = NFS3ERR_EXIST;
514         result.diropres3_u.resfail = wcc;
515         g_mutex_unlock(dir->lock);
516         async_rpc_send_reply(req, &result);
517         return;
518     }
519
520     BlueSkyInode *file;
521     file = bluesky_new_inode(bluesky_fs_alloc_inode(fs), fs, BLUESKY_DIRECTORY);
522     file->nlink = 1;
523     file->mode = 0755;
524     int64_t time = bluesky_get_current_time();
525     file->mtime = time;
526     file->ctime = time;
527     file->atime = time;
528     file->ntime = time;
529     g_mutex_lock(file->lock);
530     bluesky_insert_inode(fs, file);
531     bluesky_directory_insert(dir, argp->where.name, file->inum);
532     set_attributes(file, &argp->attributes);
533
534     bluesky_inode_update_ctime(dir, TRUE);
535
536     wcc.after.present = TRUE;
537     encode_fattr3(&wcc.after.post_op_attr_u.attributes, dir);
538     result.diropres3_u.resok.obj_attributes.present = TRUE;
539     encode_fattr3(&result.diropres3_u.resok.obj_attributes.post_op_attr_u.attributes, file);
540     result.diropres3_u.resok.dir_wcc = wcc;
541
542     uint64_t fh_bytes;
543     fh_bytes = GUINT64_TO_BE(file->inum);
544     result.diropres3_u.resok.obj.present = TRUE;
545     result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_len = 8;
546     result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_val = (char *)&fh_bytes;
547
548     g_mutex_unlock(file->lock);
549     g_mutex_unlock(dir->lock);
550     async_rpc_send_reply(req, &result);
551 }
552
553 void nfsproc3_symlink_3_svc(symlink3args *argp, RPCRequest *req)
554 {
555     diropres3 result;
556     memset(&result, 0, sizeof(result));
557     struct wcc_data wcc;
558     memset(&wcc, 0, sizeof(wcc));
559
560     BlueSkyInode *dir = lookup_fh(req, &argp->where.dir);
561     if (dir == NULL) {
562         result.status = NFS3ERR_STALE;
563         result.diropres3_u.resfail = wcc;
564         async_rpc_send_reply(req, &result);
565         return;
566     }
567     g_mutex_lock(dir->lock);
568
569     encode_pre_wcc(&wcc, dir);
570     if (dir->type != BLUESKY_DIRECTORY) {
571         result.status = NFS3ERR_NOTDIR;
572         result.diropres3_u.resfail = wcc;
573         g_mutex_unlock(dir->lock);
574         async_rpc_send_reply(req, &result);
575         return;
576     }
577
578     if (!validate_filename(argp->where.name)
579         || strcmp(argp->where.name, ".") == 0
580         || strcmp(argp->where.name, "..") == 0)
581     {
582         result.status = NFS3ERR_EXIST;
583         result.diropres3_u.resfail = wcc;
584         g_mutex_unlock(dir->lock);
585         async_rpc_send_reply(req, &result);
586         return;
587     }
588
589     BlueSkyInode *file;
590     file = bluesky_new_inode(bluesky_fs_alloc_inode(fs), fs, BLUESKY_SYMLINK);
591     file->nlink = 1;
592     file->mode = 0755;
593     int64_t time = bluesky_get_current_time();
594     file->mtime = time;
595     file->ctime = time;
596     file->atime = time;
597     file->ntime = time;
598     file->symlink_contents = g_strdup(argp->symlink.symlink_data);
599     g_mutex_lock(file->lock);
600     bluesky_insert_inode(fs, file);
601     bluesky_directory_insert(dir, argp->where.name, file->inum);
602
603     bluesky_inode_update_ctime(dir, TRUE);
604
605     wcc.after.present = TRUE;
606     encode_fattr3(&wcc.after.post_op_attr_u.attributes, dir);
607     result.diropres3_u.resok.obj_attributes.present = TRUE;
608     encode_fattr3(&result.diropres3_u.resok.obj_attributes.post_op_attr_u.attributes, file);
609     result.diropres3_u.resok.dir_wcc = wcc;
610
611     uint64_t fh_bytes;
612     fh_bytes = GUINT64_TO_BE(file->inum);
613     result.diropres3_u.resok.obj.present = TRUE;
614     result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_len = 8;
615     result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_val = (char *)&fh_bytes;
616
617     g_mutex_unlock(file->lock);
618     g_mutex_unlock(dir->lock);
619     async_rpc_send_reply(req, &result);
620 }
621
622 void nfsproc3_mknod_3_svc(mknod3args *argp, RPCRequest *req)
623 {
624     diropres3 result;
625     memset(&result, 0, sizeof(result));
626
627     result.status = NFS3ERR_NOTSUPP;
628
629     async_rpc_send_reply(req, &result);
630 }
631
632 void nfsproc3_remove_3_svc(diropargs3 *argp, RPCRequest *req)
633 {
634     wccstat3 result;
635     memset(&result, 0, sizeof(result));
636
637     BlueSkyInode *dir = lookup_fh(req, &argp->dir);
638     if (dir == NULL) {
639         result.status = NFS3ERR_STALE;
640         async_rpc_send_reply(req, &result);
641         return;
642     }
643
644     g_mutex_lock(dir->lock);
645
646     encode_pre_wcc(&result.wccstat3_u.wcc, dir);
647
648     if (!validate_filename(argp->name)
649         || strcmp(argp->name, ".") == 0
650         || strcmp(argp->name, "..") == 0)
651     {
652         result.status = NFS3ERR_NOENT;
653         g_mutex_unlock(dir->lock);
654         async_rpc_send_reply(req, &result);
655         return;
656     }
657
658     /* TODO: Decrement link count, deallocate inode if needed. */
659
660     bluesky_directory_remove(dir, argp->name);
661
662     result.status = NFS3_OK;
663     result.wccstat3_u.wcc.after.present = TRUE;
664     encode_fattr3(&result.wccstat3_u.wcc.after.post_op_attr_u.attributes,
665                   dir);
666
667     g_mutex_unlock(dir->lock);
668     async_rpc_send_reply(req, &result);
669 }
670
671 void nfsproc3_rmdir_3_svc(diropargs3 *argp, RPCRequest *req)
672 {
673     wccstat3 result;
674     memset(&result, 0, sizeof(result));
675
676     BlueSkyInode *dir = lookup_fh(req, &argp->dir);
677     if (dir == NULL) {
678         result.status = NFS3ERR_STALE;
679         async_rpc_send_reply(req, &result);
680         return;
681     }
682
683     g_mutex_lock(dir->lock);
684
685     encode_pre_wcc(&result.wccstat3_u.wcc, dir);
686
687     if (!validate_filename(argp->name)
688         || strcmp(argp->name, ".") == 0
689         || strcmp(argp->name, "..") == 0)
690     {
691         result.status = NFS3ERR_NOENT;
692         g_mutex_unlock(dir->lock);
693         async_rpc_send_reply(req, &result);
694         return;
695     }
696
697     uint64_t inum = bluesky_directory_lookup(dir, argp->name);
698     BlueSkyInode *inode = bluesky_get_inode(fs, inum);
699     if (inode == NULL) {
700         result.status = NFS3ERR_NOENT;
701         g_mutex_unlock(dir->lock);
702         async_rpc_send_reply(req, &result);
703         return;
704     }
705     g_mutex_lock(inode->lock);
706     schedule_inode_unref(req, inode);
707
708     if (inode->type != BLUESKY_DIRECTORY) {
709         result.status = NFS3ERR_NOTDIR;
710         g_mutex_unlock(inode->lock);
711         g_mutex_unlock(dir->lock);
712         async_rpc_send_reply(req, &result);
713         return;
714     }
715     if (g_sequence_get_length(inode->dirents) > 0) {
716         printf("Directory not empty: %d entries\n",
717                g_sequence_get_length(inode->dirents));
718         result.status = NFS3ERR_NOTEMPTY;
719         g_mutex_unlock(inode->lock);
720         g_mutex_unlock(dir->lock);
721         async_rpc_send_reply(req, &result);
722         return;
723     }
724
725     /* TODO: Decrement link count, deallocate inode if needed. */
726
727     bluesky_directory_remove(dir, argp->name);
728
729     result.status = NFS3_OK;
730     result.wccstat3_u.wcc.after.present = TRUE;
731     encode_fattr3(&result.wccstat3_u.wcc.after.post_op_attr_u.attributes,
732                   dir);
733
734     g_mutex_unlock(inode->lock);
735     g_mutex_unlock(dir->lock);
736     async_rpc_send_reply(req, &result);
737 }
738
739 void nfsproc3_rename_3_svc(rename3args *argp, RPCRequest *req)
740 {
741     rename3res result;
742     memset(&result, 0, sizeof(result));
743     wcc_data *wcc1 = &result.rename3res_u.res.fromdir_wcc;
744     wcc_data *wcc2 = &result.rename3res_u.res.todir_wcc;
745
746     BlueSkyInode *dir1 = lookup_fh(req, &argp->from.dir);
747     if (dir1 == NULL) {
748         result.status = NFS3ERR_STALE;
749         async_rpc_send_reply(req, &result);
750         return;
751     }
752     g_mutex_lock(dir1->lock);
753     encode_pre_wcc(wcc1, dir1);
754
755     BlueSkyInode *dir2 = lookup_fh(req, &argp->to.dir);
756     if (dir2 == NULL) {
757         result.status = NFS3ERR_STALE;
758         g_mutex_unlock(dir1->lock);
759         async_rpc_send_reply(req, &result);
760         return;
761     }
762     g_mutex_lock(dir2->lock);
763     encode_pre_wcc(wcc2, dir1);
764
765     gboolean status = bluesky_rename(dir1, argp->from.name,
766                                      dir2, argp->to.name,
767                                      TRUE, TRUE);
768
769     wcc1->after.present = TRUE;
770     encode_fattr3(&wcc1->after.post_op_attr_u.attributes, dir1);
771     wcc2->after.present = TRUE;
772     encode_fattr3(&wcc2->after.post_op_attr_u.attributes, dir2);
773     if (status)
774         result.status = NFS3_OK;
775     else
776         result.status = NFS3ERR_PERM;
777
778     g_mutex_unlock(dir2->lock);
779     g_mutex_unlock(dir1->lock);
780     async_rpc_send_reply(req, &result);
781 }
782
783 void nfsproc3_link_3_svc(link3args *argp, RPCRequest *req)
784 {
785     link3res result;
786     memset(&result, 0, sizeof(result));
787     struct wcc_data wcc;
788     memset(&wcc, 0, sizeof(wcc));
789
790     BlueSkyInode *inode = lookup_fh(req, &argp->file);
791     if (inode == NULL) {
792         result.status = NFS3ERR_STALE;
793         result.link3res_u.res.linkdir_wcc = wcc;
794         async_rpc_send_reply(req, &result);
795         return;
796     }
797     g_mutex_lock(inode->lock);
798
799     BlueSkyInode *dir = lookup_fh(req, &argp->link.dir);
800     if (dir == NULL) {
801         result.status = NFS3ERR_STALE;
802         result.link3res_u.res.linkdir_wcc = wcc;
803         g_mutex_unlock(inode->lock);
804         async_rpc_send_reply(req, &result);
805         return;
806     }
807     g_mutex_lock(dir->lock);
808
809     encode_pre_wcc(&wcc, dir);
810     if (dir->type != BLUESKY_DIRECTORY) {
811         result.status = NFS3ERR_NOTDIR;
812         result.link3res_u.res.linkdir_wcc = wcc;
813         g_mutex_unlock(inode->lock);
814         g_mutex_unlock(dir->lock);
815         async_rpc_send_reply(req, &result);
816         return;
817     }
818
819     if (!validate_filename(argp->link.name)
820         || strcmp(argp->link.name, ".") == 0
821         || strcmp(argp->link.name, "..") == 0
822         || bluesky_directory_lookup(dir, argp->link.name) != 0)
823     {
824         result.status = NFS3ERR_EXIST;
825         result.link3res_u.res.linkdir_wcc = wcc;
826         g_mutex_unlock(inode->lock);
827         g_mutex_unlock(dir->lock);
828         async_rpc_send_reply(req, &result);
829         return;
830     }
831
832     if (!bluesky_directory_insert(dir, argp->link.name, inode->inum)) {
833         result.status = NFS3ERR_EXIST;
834         result.link3res_u.res.linkdir_wcc = wcc;
835         g_mutex_unlock(inode->lock);
836         g_mutex_unlock(dir->lock);
837         async_rpc_send_reply(req, &result);
838         return;
839     }
840     inode->nlink++;
841     bluesky_inode_update_ctime(inode, FALSE);
842     bluesky_inode_update_ctime(dir, TRUE);
843
844     result.status = NFS3_OK;
845     wcc.after.present = TRUE;
846     encode_fattr3(&wcc.after.post_op_attr_u.attributes, dir);
847     result.link3res_u.res.file_attributes.present = TRUE;
848     encode_fattr3(&result.link3res_u.res.file_attributes.post_op_attr_u.attributes, inode);
849     result.link3res_u.res.linkdir_wcc = wcc;
850
851     g_mutex_unlock(inode->lock);
852     g_mutex_unlock(dir->lock);
853     async_rpc_send_reply(req, &result);
854 }
855
856 gint bluesky_dirent_compare(gconstpointer a, gconstpointer b,
857                             gpointer unused);
858
859 #define MAX_READDIR_DIRENTS 64
860 void nfsproc3_readdir_3_svc(readdir3args *argp, RPCRequest *req)
861 {
862     readdir3res result;
863     memset(&result, 0, sizeof(result));
864
865     BlueSkyInode *dir = lookup_fh(req, &argp->dir);
866     if (dir == NULL) {
867         result.status = NFS3ERR_STALE;
868         result.readdir3res_u.resfail.present = FALSE;
869         async_rpc_send_reply(req, &result);
870         return;
871     }
872     g_mutex_lock(dir->lock);
873
874     result.status = NFS3_OK;
875     result.readdir3res_u.resok.dir_attributes.present = TRUE;
876     encode_fattr3(&result.readdir3res_u.resok.dir_attributes.post_op_attr_u.attributes, dir);
877     memset(result.readdir3res_u.resok.cookieverf, 0,
878            sizeof(result.readdir3res_u.resok.cookieverf));
879
880     entry3 dirents[MAX_READDIR_DIRENTS];
881     int count = 0;
882
883     BlueSkyDirent start = {NULL, NULL, argp->cookie, 0};
884     GSequenceIter *i = g_sequence_search(dir->dirents, &start,
885                                          bluesky_dirent_compare, NULL);
886
887     while (count < MAX_READDIR_DIRENTS && !g_sequence_iter_is_end(i)) {
888         BlueSkyDirent *d = g_sequence_get(i);
889         dirents[count].fileid = d->inum;
890         dirents[count].name = d->name;
891         dirents[count].cookie = d->cookie;
892         dirents[count].nextentry = NULL;
893         if (count > 0)
894             dirents[count - 1].nextentry = &dirents[count];
895         i = g_sequence_iter_next(i);
896         count++;
897     }
898
899     if (count > 0)
900         result.readdir3res_u.resok.reply.entries = &dirents[0];
901     else
902         result.readdir3res_u.resok.reply.entries = NULL;
903     result.readdir3res_u.resok.reply.eof = g_sequence_iter_is_end(i);
904
905     g_mutex_unlock(dir->lock);
906     async_rpc_send_reply(req, &result);
907 }
908
909 void nfsproc3_readdirplus_3_svc(readdirplus3args *argp, RPCRequest *req)
910 {
911     /* XDR-encoded sizes:
912      *   post_op_attr: 88 bytes
913      *   base readdirplus3resok: 88 + 16 bytes
914      *   base directory entry: 24 bytes + filename
915      *   attributes/fh3: 88 + 8 + filehandle size
916      */
917     size_t dircount = 88 + 16, attrcount = 0;
918     readdirplus3res result;
919     memset(&result, 0, sizeof(result));
920
921     BlueSkyInode *dir = lookup_fh(req, &argp->dir);
922     if (dir == NULL) {
923         result.status = NFS3ERR_STALE;
924         result.readdirplus3res_u.resfail.present = FALSE;
925         async_rpc_send_reply(req, &result);
926         return;
927     }
928     g_mutex_lock(dir->lock);
929
930     result.status = NFS3_OK;
931     result.readdirplus3res_u.resok.dir_attributes.present = TRUE;
932     encode_fattr3(&result.readdirplus3res_u.resok.dir_attributes.post_op_attr_u.attributes, dir);
933     memset(result.readdirplus3res_u.resok.cookieverf, 0,
934            sizeof(result.readdirplus3res_u.resok.cookieverf));
935
936     entryplus3 dirents[MAX_READDIR_DIRENTS];
937     uint64_t fh_bytes[MAX_READDIR_DIRENTS];
938     int count = 0;
939
940     /* TODO: Handle dircount, maxcount arguments from client. */
941
942     BlueSkyDirent start = {NULL, NULL, argp->cookie, 0};
943     GSequenceIter *i = g_sequence_search(dir->dirents, &start,
944                                          bluesky_dirent_compare, NULL);
945
946     while (count < MAX_READDIR_DIRENTS && !g_sequence_iter_is_end(i)) {
947         BlueSkyDirent *d = g_sequence_get(i);
948         BlueSkyInode *inode = bluesky_get_inode(fs, d->inum);
949         g_mutex_lock(inode->lock);
950         if (inode != NULL) {
951             dircount += 24 + ((strlen(d->name) + 3) & ~3);
952             attrcount += 88 + 8 + 8;
953             if (dircount > argp->dircount
954                 || dircount + attrcount > argp->maxcount)
955             {
956                 g_mutex_unlock(inode->lock);
957                 bluesky_inode_unref(inode);
958                 break;
959             }
960             dirents[count].fileid = d->inum;
961             dirents[count].name = d->name;
962             dirents[count].cookie = d->cookie;
963             dirents[count].nextentry = NULL;
964             dirents[count].name_attributes.present = TRUE;
965             encode_fattr3(&dirents[count].name_attributes.post_op_attr_u.attributes, inode);
966             fh_bytes[count] = GUINT64_TO_BE(d->inum);
967             dirents[count].name_handle.present = TRUE;
968             dirents[count].name_handle.post_op_fh3_u.handle.data.data_len = 8;
969             dirents[count].name_handle.post_op_fh3_u.handle.data.data_val
970                 = (char *)&fh_bytes[count];
971             if (count > 0)
972                 dirents[count - 1].nextentry = &dirents[count];
973             count++;
974             g_mutex_unlock(inode->lock);
975             bluesky_inode_unref(inode);
976         }
977         i = g_sequence_iter_next(i);
978     }
979
980     if (count > 0)
981         result.readdirplus3res_u.resok.reply.entries = &dirents[0];
982     else
983         result.readdirplus3res_u.resok.reply.entries = NULL;
984     result.readdirplus3res_u.resok.reply.eof = g_sequence_iter_is_end(i);
985
986     g_mutex_unlock(dir->lock);
987     async_rpc_send_reply(req, &result);
988 }
989
990 void nfsproc3_fsstat_3_svc(nfs_fh3 *argp, RPCRequest *req)
991 {
992     fsstat3res result;
993     memset(&result, 0, sizeof(result));
994
995     BlueSkyInode *inode = lookup_fh(req, argp);
996     if (inode == NULL) {
997         result.status = NFS3ERR_STALE;
998         result.fsstat3res_u.resfail.present = FALSE;
999         async_rpc_send_reply(req, &result);
1000         return;
1001     }
1002     g_mutex_lock(inode->lock);
1003
1004     result.status = NFS3_OK;
1005     result.fsstat3res_u.resok.obj_attributes.present = TRUE;
1006     encode_fattr3(&result.fsstat3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
1007
1008     result.fsstat3res_u.resok.tbytes = (1 << 30);
1009     result.fsstat3res_u.resok.fbytes = (1 << 30);
1010     result.fsstat3res_u.resok.abytes = (1 << 30);
1011     result.fsstat3res_u.resok.tfiles = 0;
1012     result.fsstat3res_u.resok.ffiles = 0;
1013     result.fsstat3res_u.resok.afiles = 0;
1014     result.fsstat3res_u.resok.invarsec = 0;
1015
1016     g_mutex_unlock(inode->lock);
1017     async_rpc_send_reply(req, &result);
1018 }
1019
1020 void nfsproc3_fsinfo_3_svc(nfs_fh3 *argp, RPCRequest *req)
1021 {
1022     fsinfo3res result;
1023     memset(&result, 0, sizeof(result));
1024
1025     BlueSkyInode *inode = bluesky_get_inode(fs, 1);
1026     g_mutex_lock(inode->lock);
1027     result.status = NFS3_OK;
1028     result.fsinfo3res_u.resok.obj_attributes.present = TRUE;
1029     encode_fattr3(&result.fsinfo3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
1030     result.fsinfo3res_u.resok.rtmax = 32768;
1031     result.fsinfo3res_u.resok.rtpref = 32768;
1032     result.fsinfo3res_u.resok.rtmult = 4096;
1033     result.fsinfo3res_u.resok.wtmax = 32768;
1034     result.fsinfo3res_u.resok.wtpref = 32768;
1035     result.fsinfo3res_u.resok.wtmult = 4096;
1036     result.fsinfo3res_u.resok.dtpref = 4096;
1037     result.fsinfo3res_u.resok.maxfilesize = 0x7fffffffffffffffULL;
1038     result.fsinfo3res_u.resok.time_delta.seconds = 0;
1039     result.fsinfo3res_u.resok.time_delta.nseconds = 1000;
1040     result.fsinfo3res_u.resok.properties
1041         = FSF3_LINK | FSF3_SYMLINK | FSF3_HOMOGENEOUS | FSF3_CANSETTIME;
1042
1043     g_mutex_unlock(inode->lock);
1044     bluesky_inode_unref(inode);
1045     async_rpc_send_reply(req, &result);
1046 }
1047
1048 void nfsproc3_pathconf_3_svc(nfs_fh3 *argp, RPCRequest *req)
1049 {
1050     pathconf3res result;
1051     memset(&result, 0, sizeof(result));
1052
1053     BlueSkyInode *inode = bluesky_get_inode(fs, 1);
1054     g_mutex_lock(inode->lock);
1055     result.status = NFS3_OK;
1056     result.pathconf3res_u.resok.obj_attributes.present = TRUE;
1057     encode_fattr3(&result.pathconf3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
1058     result.pathconf3res_u.resok.linkmax = 0xffffffff;
1059     result.pathconf3res_u.resok.name_max = 255;
1060     result.pathconf3res_u.resok.no_trunc = TRUE;
1061     result.pathconf3res_u.resok.chown_restricted = TRUE;
1062     result.pathconf3res_u.resok.case_insensitive = FALSE;
1063     result.pathconf3res_u.resok.case_preserving = TRUE;
1064
1065     g_mutex_unlock(inode->lock);
1066     bluesky_inode_unref(inode);
1067     async_rpc_send_reply(req, &result);
1068 }
1069
1070 void nfsproc3_commit_3_svc(commit3args *argp, RPCRequest *req)
1071 {
1072     commit3res result;
1073     memset(&result, 0, sizeof(result));
1074
1075     result.status = NFS3ERR_NOTSUPP;
1076
1077     BlueSkyInode *inode = lookup_fh(req, &argp->file);
1078     if (inode == NULL) {
1079         result.status = NFS3ERR_STALE;
1080         async_rpc_send_reply(req, &result);
1081         return;
1082     }
1083
1084     g_mutex_lock(inode->lock);
1085     encode_pre_wcc(&result.commit3res_u.resok.file_wcc, inode);
1086
1087     bluesky_inode_do_sync(inode);
1088
1089     result.commit3res_u.resok.file_wcc.after.present = TRUE;
1090     encode_fattr3(&result.commit3res_u.resok.file_wcc.after.post_op_attr_u.attributes, inode);
1091
1092     g_mutex_unlock(inode->lock);
1093
1094     async_rpc_send_reply(req, &result);
1095 }