2895e5b9d96ebd29b0bf0ad9c0dabcf794277042
[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
247     result.lookup3res_u.resok.dir_attributes.present = TRUE;
248     encode_fattr3(&result.lookup3res_u.resok.dir_attributes.post_op_attr_u.attributes, dir);
249     g_mutex_unlock(dir->lock);
250
251     BlueSkyInode *inode = bluesky_get_inode(fs, inum);
252     if (inode == NULL) {
253         result.status = NFS3ERR_NOENT;
254         async_rpc_send_reply(req, &result);
255         return;
256     }
257     g_mutex_lock(inode->lock);
258     schedule_inode_unref(req, inode);
259
260     result.status = NFS3_OK;
261     result.lookup3res_u.resok.obj_attributes.present = TRUE;
262     encode_fattr3(&result.lookup3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
263
264     uint64_t fh_bytes;
265     fh_bytes = GUINT64_TO_BE(inum);
266     result.lookup3res_u.resok.object.data.data_len = 8;
267     result.lookup3res_u.resok.object.data.data_val = (char *)&fh_bytes;
268
269     g_mutex_unlock(inode->lock);
270     async_rpc_send_reply(req, &result);
271 }
272
273 void nfsproc3_access_3_svc(access3args *argp, RPCRequest *req)
274 {
275     access3res result;
276     memset(&result, 0, sizeof(result));
277
278     BlueSkyInode *inode = lookup_fh(req, &argp->object);
279     if (inode == NULL) {
280         result.status = NFS3ERR_STALE;
281         result.access3res_u.resfail.present = FALSE;
282         async_rpc_send_reply(req, &result);
283         return;
284     }
285
286     g_mutex_lock(inode->lock);
287     result.status = NFS3_OK;
288     result.access3res_u.resok.obj_attributes.present = TRUE;
289     encode_fattr3(&result.access3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
290     result.access3res_u.resok.access = argp->access;
291     g_mutex_unlock(inode->lock);
292
293     async_rpc_send_reply(req, &result);
294 }
295
296 void nfsproc3_readlink_3_svc(nfs_fh3 *argp, RPCRequest *req)
297 {
298     readlink3res result;
299     memset(&result, 0, sizeof(result));
300
301     BlueSkyInode *inode = lookup_fh(req, argp);
302     if (inode != NULL) {
303         g_mutex_lock(inode->lock);
304         if (inode->type == BLUESKY_SYMLINK) {
305             result.status = NFS3_OK;
306             result.readlink3res_u.resok.symlink_attributes.present = TRUE;
307             encode_fattr3(&result.readlink3res_u.resok.symlink_attributes.post_op_attr_u.attributes, inode);
308             result.readlink3res_u.resok.data = inode->symlink_contents;
309         } else {
310             result.status = NFS3ERR_INVAL;
311             result.readlink3res_u.resfail.present = TRUE;
312             encode_fattr3(&result.readlink3res_u.resfail.post_op_attr_u.attributes, inode);
313         }
314         g_mutex_unlock(inode->lock);
315     } else {
316         result.status = NFS3ERR_STALE;
317     }
318
319     async_rpc_send_reply(req, &result);
320 }
321
322 void nfsproc3_read_3_svc(read3args *argp, RPCRequest *req)
323 {
324     read3res result;
325     memset(&result, 0, sizeof(result));
326     char buf[32768];
327
328     BlueSkyInode *inode = lookup_fh(req, &argp->file);
329     if (inode == NULL) {
330         result.status = NFS3ERR_STALE;
331         result.read3res_u.resfail.present = FALSE;
332         async_rpc_send_reply(req, &result);
333         return;
334     }
335
336     g_mutex_lock(inode->lock);
337
338     int count = argp->count;
339     if (argp->offset >= inode->size) {
340         count = 0;
341         result.read3res_u.resok.eof = TRUE;
342     } else {
343         count = MIN(count, inode->size - argp->offset);
344         if (argp->offset + count == inode->size)
345             result.read3res_u.resok.eof = TRUE;
346         else
347             result.read3res_u.resok.eof = FALSE;
348
349         bluesky_file_read(inode, argp->offset, buf, count);
350     }
351
352     result.status = NFS3_OK;
353     result.read3res_u.resok.file_attributes.present = TRUE;
354     encode_fattr3(&result.read3res_u.resok.file_attributes.post_op_attr_u.attributes, inode);
355     result.read3res_u.resok.count = count;
356     result.read3res_u.resok.data.data_val = buf;
357     result.read3res_u.resok.data.data_len = count;
358
359     g_mutex_unlock(inode->lock);
360
361     async_rpc_send_reply(req, &result);
362 }
363
364 void nfsproc3_write_3_svc(write3args *argp, RPCRequest *req)
365 {
366     write3res result;
367     memset(&result, 0, sizeof(result));
368     struct wcc_data wcc;
369     memset(&wcc, 0, sizeof(wcc));
370
371     BlueSkyInode *inode = lookup_fh(req, &argp->file);
372     if (inode == NULL) {
373         result.status = NFS3ERR_STALE;
374         result.write3res_u.resfail = wcc;
375         async_rpc_send_reply(req, &result);
376         return;
377     }
378
379     g_mutex_lock(inode->lock);
380
381     encode_pre_wcc(&wcc, inode);
382     if (inode->type != BLUESKY_REGULAR) {
383         result.status = NFS3ERR_INVAL;
384         result.write3res_u.resfail = wcc;
385         g_mutex_unlock(inode->lock);
386         async_rpc_send_reply(req, &result);
387         return;
388     }
389
390     uint64_t lastbyte = argp->offset + argp->count;
391     if (lastbyte > inode->size) {
392         bluesky_file_truncate(inode, lastbyte);
393     }
394
395     if (argp->data.data_len < argp->count) {
396         /* ??? */
397     } else {
398         bluesky_file_write(inode, argp->offset,
399                            argp->data.data_val, argp->count);
400     }
401
402     wcc.after.present = TRUE;
403     encode_fattr3(&wcc.after.post_op_attr_u.attributes, inode);
404     result.write3res_u.resok.file_wcc = wcc;
405     result.write3res_u.resok.count = argp->count;
406     result.write3res_u.resok.committed = FILE_SYNC;
407
408     g_mutex_unlock(inode->lock);
409
410     async_rpc_send_reply(req, &result);
411 }
412
413 void nfsproc3_create_3_svc(create3args *argp, RPCRequest *req)
414 {
415     diropres3 result;
416     memset(&result, 0, sizeof(result));
417     struct wcc_data wcc;
418     memset(&wcc, 0, sizeof(wcc));
419
420     BlueSkyInode *dir = lookup_fh(req, &argp->where.dir);
421     if (dir == NULL) {
422         result.status = NFS3ERR_STALE;
423         result.diropres3_u.resfail = wcc;
424         async_rpc_send_reply(req, &result);
425         return;
426     }
427
428     g_mutex_lock(dir->lock);
429
430     encode_pre_wcc(&wcc, dir);
431     if (dir->type != BLUESKY_DIRECTORY) {
432         result.status = NFS3ERR_NOTDIR;
433         result.diropres3_u.resfail = wcc;
434         g_mutex_unlock(dir->lock);
435         async_rpc_send_reply(req, &result);
436         return;
437     }
438
439     if (!validate_filename(argp->where.name)
440         || strcmp(argp->where.name, ".") == 0
441         || strcmp(argp->where.name, "..") == 0)
442     {
443         result.status = NFS3ERR_EXIST;
444         result.diropres3_u.resfail = wcc;
445         g_mutex_unlock(dir->lock);
446         async_rpc_send_reply(req, &result);
447         return;
448     }
449
450     BlueSkyInode *file;
451     file = bluesky_new_inode(bluesky_fs_alloc_inode(fs), fs, BLUESKY_REGULAR);
452     file->nlink = 1;
453     file->mode = 0755;
454     int64_t time = bluesky_get_current_time();
455     printf("time: %"PRIi64"\n", time);
456     file->mtime = time;
457     file->ctime = time;
458     file->atime = time;
459     file->ntime = time;
460     g_mutex_lock(file->lock);
461     bluesky_insert_inode(fs, file);
462     bluesky_directory_insert(dir, argp->where.name, file->inum);
463
464     bluesky_inode_update_ctime(dir, TRUE);
465
466     wcc.after.present = TRUE;
467     encode_fattr3(&wcc.after.post_op_attr_u.attributes, dir);
468     result.diropres3_u.resok.obj_attributes.present = TRUE;
469     encode_fattr3(&result.diropres3_u.resok.obj_attributes.post_op_attr_u.attributes, file);
470     result.diropres3_u.resok.dir_wcc = wcc;
471
472     uint64_t fh_bytes;
473     fh_bytes = GUINT64_TO_BE(file->inum);
474     result.diropres3_u.resok.obj.present = TRUE;
475     result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_len = 8;
476     result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_val = (char *)&fh_bytes;
477
478     g_mutex_unlock(file->lock);
479     g_mutex_unlock(dir->lock);
480
481     async_rpc_send_reply(req, &result);
482 }
483
484 void nfsproc3_mkdir_3_svc(mkdir3args *argp, RPCRequest *req)
485 {
486     diropres3 result;
487     memset(&result, 0, sizeof(result));
488     struct wcc_data wcc;
489     memset(&wcc, 0, sizeof(wcc));
490
491     BlueSkyInode *dir = lookup_fh(req, &argp->where.dir);
492     if (dir == NULL) {
493         result.status = NFS3ERR_STALE;
494         result.diropres3_u.resfail = wcc;
495         async_rpc_send_reply(req, &result);
496         return;
497     }
498
499     g_mutex_lock(dir->lock);
500
501     encode_pre_wcc(&wcc, dir);
502     if (dir->type != BLUESKY_DIRECTORY) {
503         result.status = NFS3ERR_NOTDIR;
504         result.diropres3_u.resfail = wcc;
505         g_mutex_unlock(dir->lock);
506         async_rpc_send_reply(req, &result);
507         return;
508     }
509
510     if (!validate_filename(argp->where.name)
511         || strcmp(argp->where.name, ".") == 0
512         || strcmp(argp->where.name, "..") == 0)
513     {
514         result.status = NFS3ERR_EXIST;
515         result.diropres3_u.resfail = wcc;
516         g_mutex_unlock(dir->lock);
517         async_rpc_send_reply(req, &result);
518         return;
519     }
520
521     BlueSkyInode *file;
522     file = bluesky_new_inode(bluesky_fs_alloc_inode(fs), fs, BLUESKY_DIRECTORY);
523     file->nlink = 1;
524     file->mode = 0755;
525     int64_t time = bluesky_get_current_time();
526     file->mtime = time;
527     file->ctime = time;
528     file->atime = time;
529     file->ntime = time;
530     g_mutex_lock(file->lock);
531     bluesky_insert_inode(fs, file);
532     bluesky_directory_insert(dir, argp->where.name, file->inum);
533     set_attributes(file, &argp->attributes);
534
535     bluesky_inode_update_ctime(dir, TRUE);
536
537     wcc.after.present = TRUE;
538     encode_fattr3(&wcc.after.post_op_attr_u.attributes, dir);
539     result.diropres3_u.resok.obj_attributes.present = TRUE;
540     encode_fattr3(&result.diropres3_u.resok.obj_attributes.post_op_attr_u.attributes, file);
541     result.diropres3_u.resok.dir_wcc = wcc;
542
543     uint64_t fh_bytes;
544     fh_bytes = GUINT64_TO_BE(file->inum);
545     result.diropres3_u.resok.obj.present = TRUE;
546     result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_len = 8;
547     result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_val = (char *)&fh_bytes;
548
549     g_mutex_unlock(file->lock);
550     g_mutex_unlock(dir->lock);
551     async_rpc_send_reply(req, &result);
552 }
553
554 void nfsproc3_symlink_3_svc(symlink3args *argp, RPCRequest *req)
555 {
556     diropres3 result;
557     memset(&result, 0, sizeof(result));
558     struct wcc_data wcc;
559     memset(&wcc, 0, sizeof(wcc));
560
561     BlueSkyInode *dir = lookup_fh(req, &argp->where.dir);
562     if (dir == NULL) {
563         result.status = NFS3ERR_STALE;
564         result.diropres3_u.resfail = wcc;
565         async_rpc_send_reply(req, &result);
566         return;
567     }
568     g_mutex_lock(dir->lock);
569
570     encode_pre_wcc(&wcc, dir);
571     if (dir->type != BLUESKY_DIRECTORY) {
572         result.status = NFS3ERR_NOTDIR;
573         result.diropres3_u.resfail = wcc;
574         g_mutex_unlock(dir->lock);
575         async_rpc_send_reply(req, &result);
576         return;
577     }
578
579     if (!validate_filename(argp->where.name)
580         || strcmp(argp->where.name, ".") == 0
581         || strcmp(argp->where.name, "..") == 0)
582     {
583         result.status = NFS3ERR_EXIST;
584         result.diropres3_u.resfail = wcc;
585         g_mutex_unlock(dir->lock);
586         async_rpc_send_reply(req, &result);
587         return;
588     }
589
590     BlueSkyInode *file;
591     file = bluesky_new_inode(bluesky_fs_alloc_inode(fs), fs, BLUESKY_SYMLINK);
592     file->nlink = 1;
593     file->mode = 0755;
594     int64_t time = bluesky_get_current_time();
595     file->mtime = time;
596     file->ctime = time;
597     file->atime = time;
598     file->ntime = time;
599     file->symlink_contents = g_strdup(argp->symlink.symlink_data);
600     g_mutex_lock(file->lock);
601     bluesky_insert_inode(fs, file);
602     bluesky_directory_insert(dir, argp->where.name, file->inum);
603
604     bluesky_inode_update_ctime(dir, TRUE);
605
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;
611
612     uint64_t fh_bytes;
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;
617
618     g_mutex_unlock(file->lock);
619     g_mutex_unlock(dir->lock);
620     async_rpc_send_reply(req, &result);
621 }
622
623 void nfsproc3_mknod_3_svc(mknod3args *argp, RPCRequest *req)
624 {
625     diropres3 result;
626     memset(&result, 0, sizeof(result));
627
628     result.status = NFS3ERR_NOTSUPP;
629
630     async_rpc_send_reply(req, &result);
631 }
632
633 void nfsproc3_remove_3_svc(diropargs3 *argp, RPCRequest *req)
634 {
635     wccstat3 result;
636     memset(&result, 0, sizeof(result));
637
638     BlueSkyInode *dir = lookup_fh(req, &argp->dir);
639     if (dir == NULL) {
640         result.status = NFS3ERR_STALE;
641         async_rpc_send_reply(req, &result);
642         return;
643     }
644
645     g_mutex_lock(dir->lock);
646
647     encode_pre_wcc(&result.wccstat3_u.wcc, dir);
648
649     if (!validate_filename(argp->name)
650         || strcmp(argp->name, ".") == 0
651         || strcmp(argp->name, "..") == 0)
652     {
653         result.status = NFS3ERR_NOENT;
654         g_mutex_unlock(dir->lock);
655         async_rpc_send_reply(req, &result);
656         return;
657     }
658
659     /* TODO: Decrement link count, deallocate inode if needed. */
660
661     bluesky_directory_remove(dir, argp->name);
662
663     result.status = NFS3_OK;
664     result.wccstat3_u.wcc.after.present = TRUE;
665     encode_fattr3(&result.wccstat3_u.wcc.after.post_op_attr_u.attributes,
666                   dir);
667
668     g_mutex_unlock(dir->lock);
669     async_rpc_send_reply(req, &result);
670 }
671
672 void nfsproc3_rmdir_3_svc(diropargs3 *argp, RPCRequest *req)
673 {
674     wccstat3 result;
675     memset(&result, 0, sizeof(result));
676
677     BlueSkyInode *dir = lookup_fh(req, &argp->dir);
678     if (dir == NULL) {
679         result.status = NFS3ERR_STALE;
680         async_rpc_send_reply(req, &result);
681         return;
682     }
683
684     g_mutex_lock(dir->lock);
685
686     encode_pre_wcc(&result.wccstat3_u.wcc, dir);
687
688     if (!validate_filename(argp->name)
689         || strcmp(argp->name, ".") == 0
690         || strcmp(argp->name, "..") == 0)
691     {
692         result.status = NFS3ERR_NOENT;
693         g_mutex_unlock(dir->lock);
694         async_rpc_send_reply(req, &result);
695         return;
696     }
697
698     uint64_t inum = bluesky_directory_lookup(dir, argp->name);
699     BlueSkyInode *inode = bluesky_get_inode(fs, inum);
700     if (inode == NULL) {
701         result.status = NFS3ERR_NOENT;
702         g_mutex_unlock(dir->lock);
703         async_rpc_send_reply(req, &result);
704         return;
705     }
706     g_mutex_lock(inode->lock);
707     schedule_inode_unref(req, inode);
708
709     if (inode->type != BLUESKY_DIRECTORY) {
710         result.status = NFS3ERR_NOTDIR;
711         g_mutex_unlock(inode->lock);
712         g_mutex_unlock(dir->lock);
713         async_rpc_send_reply(req, &result);
714         return;
715     }
716     if (g_sequence_get_length(inode->dirents) > 0) {
717         printf("Directory not empty: %d entries\n",
718                g_sequence_get_length(inode->dirents));
719         result.status = NFS3ERR_NOTEMPTY;
720         g_mutex_unlock(inode->lock);
721         g_mutex_unlock(dir->lock);
722         async_rpc_send_reply(req, &result);
723         return;
724     }
725
726     /* TODO: Decrement link count, deallocate inode if needed. */
727
728     bluesky_directory_remove(dir, argp->name);
729
730     result.status = NFS3_OK;
731     result.wccstat3_u.wcc.after.present = TRUE;
732     encode_fattr3(&result.wccstat3_u.wcc.after.post_op_attr_u.attributes,
733                   dir);
734
735     g_mutex_unlock(inode->lock);
736     g_mutex_unlock(dir->lock);
737     async_rpc_send_reply(req, &result);
738 }
739
740 void nfsproc3_rename_3_svc(rename3args *argp, RPCRequest *req)
741 {
742     rename3res result;
743     memset(&result, 0, sizeof(result));
744     wcc_data *wcc1 = &result.rename3res_u.res.fromdir_wcc;
745     wcc_data *wcc2 = &result.rename3res_u.res.todir_wcc;
746
747     BlueSkyInode *dir1 = lookup_fh(req, &argp->from.dir);
748     if (dir1 == NULL) {
749         result.status = NFS3ERR_STALE;
750         async_rpc_send_reply(req, &result);
751         return;
752     }
753     g_mutex_lock(dir1->lock);
754     encode_pre_wcc(wcc1, dir1);
755
756     BlueSkyInode *dir2 = lookup_fh(req, &argp->to.dir);
757     if (dir2 == NULL) {
758         result.status = NFS3ERR_STALE;
759         g_mutex_unlock(dir1->lock);
760         async_rpc_send_reply(req, &result);
761         return;
762     }
763     g_mutex_lock(dir2->lock);
764     encode_pre_wcc(wcc2, dir1);
765
766     gboolean status = bluesky_rename(dir1, argp->from.name,
767                                      dir2, argp->to.name,
768                                      TRUE, TRUE);
769
770     wcc1->after.present = TRUE;
771     encode_fattr3(&wcc1->after.post_op_attr_u.attributes, dir1);
772     wcc2->after.present = TRUE;
773     encode_fattr3(&wcc2->after.post_op_attr_u.attributes, dir2);
774     if (status)
775         result.status = NFS3_OK;
776     else
777         result.status = NFS3ERR_PERM;
778
779     g_mutex_unlock(dir2->lock);
780     g_mutex_unlock(dir1->lock);
781     async_rpc_send_reply(req, &result);
782 }
783
784 void nfsproc3_link_3_svc(link3args *argp, RPCRequest *req)
785 {
786     link3res result;
787     memset(&result, 0, sizeof(result));
788     struct wcc_data wcc;
789     memset(&wcc, 0, sizeof(wcc));
790
791     BlueSkyInode *inode = lookup_fh(req, &argp->file);
792     if (inode == NULL) {
793         result.status = NFS3ERR_STALE;
794         result.link3res_u.res.linkdir_wcc = wcc;
795         async_rpc_send_reply(req, &result);
796         return;
797     }
798     g_mutex_lock(inode->lock);
799
800     BlueSkyInode *dir = lookup_fh(req, &argp->link.dir);
801     if (dir == NULL) {
802         result.status = NFS3ERR_STALE;
803         result.link3res_u.res.linkdir_wcc = wcc;
804         g_mutex_unlock(inode->lock);
805         async_rpc_send_reply(req, &result);
806         return;
807     }
808     g_mutex_lock(dir->lock);
809
810     encode_pre_wcc(&wcc, dir);
811     if (dir->type != BLUESKY_DIRECTORY) {
812         result.status = NFS3ERR_NOTDIR;
813         result.link3res_u.res.linkdir_wcc = wcc;
814         g_mutex_unlock(inode->lock);
815         g_mutex_unlock(dir->lock);
816         async_rpc_send_reply(req, &result);
817         return;
818     }
819
820     if (!validate_filename(argp->link.name)
821         || strcmp(argp->link.name, ".") == 0
822         || strcmp(argp->link.name, "..") == 0
823         || bluesky_directory_lookup(dir, argp->link.name) != 0)
824     {
825         result.status = NFS3ERR_EXIST;
826         result.link3res_u.res.linkdir_wcc = wcc;
827         g_mutex_unlock(inode->lock);
828         g_mutex_unlock(dir->lock);
829         async_rpc_send_reply(req, &result);
830         return;
831     }
832
833     if (!bluesky_directory_insert(dir, argp->link.name, inode->inum)) {
834         result.status = NFS3ERR_EXIST;
835         result.link3res_u.res.linkdir_wcc = wcc;
836         g_mutex_unlock(inode->lock);
837         g_mutex_unlock(dir->lock);
838         async_rpc_send_reply(req, &result);
839         return;
840     }
841     inode->nlink++;
842     bluesky_inode_update_ctime(inode, FALSE);
843     bluesky_inode_update_ctime(dir, TRUE);
844
845     result.status = NFS3_OK;
846     wcc.after.present = TRUE;
847     encode_fattr3(&wcc.after.post_op_attr_u.attributes, dir);
848     result.link3res_u.res.file_attributes.present = TRUE;
849     encode_fattr3(&result.link3res_u.res.file_attributes.post_op_attr_u.attributes, inode);
850     result.link3res_u.res.linkdir_wcc = wcc;
851
852     g_mutex_unlock(inode->lock);
853     g_mutex_unlock(dir->lock);
854     async_rpc_send_reply(req, &result);
855 }
856
857 gint bluesky_dirent_compare(gconstpointer a, gconstpointer b,
858                             gpointer unused);
859
860 #define MAX_READDIR_DIRENTS 64
861 void nfsproc3_readdir_3_svc(readdir3args *argp, RPCRequest *req)
862 {
863     readdir3res result;
864     memset(&result, 0, sizeof(result));
865
866     BlueSkyInode *dir = lookup_fh(req, &argp->dir);
867     if (dir == NULL) {
868         result.status = NFS3ERR_STALE;
869         result.readdir3res_u.resfail.present = FALSE;
870         async_rpc_send_reply(req, &result);
871         return;
872     }
873     g_mutex_lock(dir->lock);
874
875     result.status = NFS3_OK;
876     result.readdir3res_u.resok.dir_attributes.present = TRUE;
877     encode_fattr3(&result.readdir3res_u.resok.dir_attributes.post_op_attr_u.attributes, dir);
878     memset(result.readdir3res_u.resok.cookieverf, 0,
879            sizeof(result.readdir3res_u.resok.cookieverf));
880
881     entry3 dirents[MAX_READDIR_DIRENTS];
882     int count = 0;
883
884     BlueSkyDirent start = {NULL, NULL, argp->cookie, 0};
885     GSequenceIter *i = g_sequence_search(dir->dirents, &start,
886                                          bluesky_dirent_compare, NULL);
887
888     while (count < MAX_READDIR_DIRENTS && !g_sequence_iter_is_end(i)) {
889         BlueSkyDirent *d = g_sequence_get(i);
890         dirents[count].fileid = d->inum;
891         dirents[count].name = d->name;
892         dirents[count].cookie = d->cookie;
893         dirents[count].nextentry = NULL;
894         if (count > 0)
895             dirents[count - 1].nextentry = &dirents[count];
896         i = g_sequence_iter_next(i);
897         count++;
898     }
899
900     if (count > 0)
901         result.readdir3res_u.resok.reply.entries = &dirents[0];
902     else
903         result.readdir3res_u.resok.reply.entries = NULL;
904     result.readdir3res_u.resok.reply.eof = g_sequence_iter_is_end(i);
905
906     g_mutex_unlock(dir->lock);
907     async_rpc_send_reply(req, &result);
908 }
909
910 void nfsproc3_readdirplus_3_svc(readdirplus3args *argp, RPCRequest *req)
911 {
912     /* XDR-encoded sizes:
913      *   post_op_attr: 88 bytes
914      *   base readdirplus3resok: 88 + 16 bytes
915      *   base directory entry: 24 bytes + filename
916      *   attributes/fh3: 88 + 8 + filehandle size
917      */
918     size_t dircount = 88 + 16, attrcount = 0;
919     readdirplus3res result;
920     memset(&result, 0, sizeof(result));
921
922     BlueSkyInode *dir = lookup_fh(req, &argp->dir);
923     if (dir == NULL) {
924         result.status = NFS3ERR_STALE;
925         result.readdirplus3res_u.resfail.present = FALSE;
926         async_rpc_send_reply(req, &result);
927         return;
928     }
929     g_mutex_lock(dir->lock);
930
931     result.status = NFS3_OK;
932     result.readdirplus3res_u.resok.dir_attributes.present = TRUE;
933     encode_fattr3(&result.readdirplus3res_u.resok.dir_attributes.post_op_attr_u.attributes, dir);
934     memset(result.readdirplus3res_u.resok.cookieverf, 0,
935            sizeof(result.readdirplus3res_u.resok.cookieverf));
936
937     entryplus3 dirents[MAX_READDIR_DIRENTS];
938     uint64_t fh_bytes[MAX_READDIR_DIRENTS];
939     int count = 0;
940
941     GSequenceIter *i;
942     BlueSkyDirent start = {NULL, NULL, argp->cookie, 0};
943
944     /* Perform a prefetch pass on inodes: for all the inodes we think we will
945      * return information about, try to load each one but don't wait.  This
946      * should let multiple inodes be fetched in parallel, instead of
947      * sequentially in the loop that follows. */
948     i = g_sequence_search(dir->dirents, &start, bluesky_dirent_compare, NULL);
949     while (count < MAX_READDIR_DIRENTS
950            && !g_sequence_iter_is_end(i)
951            && dircount <= argp->dircount
952            && dircount + attrcount <= argp->maxcount)
953     {
954         BlueSkyDirent *d = g_sequence_get(i);
955         BlueSkyInode *inode = bluesky_get_inode(fs, d->inum);
956         if (inode != NULL)
957             bluesky_inode_unref(inode);
958         dircount += 24 + ((strlen(d->name) + 3) & ~3);
959         attrcount += 88 + 8 + 8;
960         i = g_sequence_iter_next(i);
961     }
962
963     i = g_sequence_search(dir->dirents, &start, bluesky_dirent_compare, NULL);
964     count = 0;
965     dircount = 88 + 16;
966     attrcount = 0;
967     while (count < MAX_READDIR_DIRENTS && !g_sequence_iter_is_end(i)) {
968         BlueSkyDirent *d = g_sequence_get(i);
969         BlueSkyInode *inode = bluesky_get_inode(fs, d->inum);
970         g_mutex_lock(inode->lock);
971         if (inode != NULL) {
972             dircount += 24 + ((strlen(d->name) + 3) & ~3);
973             attrcount += 88 + 8 + 8;
974             if (dircount > argp->dircount
975                 || dircount + attrcount > argp->maxcount)
976             {
977                 g_mutex_unlock(inode->lock);
978                 bluesky_inode_unref(inode);
979                 break;
980             }
981             dirents[count].fileid = d->inum;
982             dirents[count].name = d->name;
983             dirents[count].cookie = d->cookie;
984             dirents[count].nextentry = NULL;
985             dirents[count].name_attributes.present = TRUE;
986             encode_fattr3(&dirents[count].name_attributes.post_op_attr_u.attributes, inode);
987             fh_bytes[count] = GUINT64_TO_BE(d->inum);
988             dirents[count].name_handle.present = TRUE;
989             dirents[count].name_handle.post_op_fh3_u.handle.data.data_len = 8;
990             dirents[count].name_handle.post_op_fh3_u.handle.data.data_val
991                 = (char *)&fh_bytes[count];
992             if (count > 0)
993                 dirents[count - 1].nextentry = &dirents[count];
994             count++;
995             g_mutex_unlock(inode->lock);
996             bluesky_inode_unref(inode);
997         }
998         i = g_sequence_iter_next(i);
999     }
1000
1001     if (count > 0)
1002         result.readdirplus3res_u.resok.reply.entries = &dirents[0];
1003     else
1004         result.readdirplus3res_u.resok.reply.entries = NULL;
1005     result.readdirplus3res_u.resok.reply.eof = g_sequence_iter_is_end(i);
1006
1007     g_mutex_unlock(dir->lock);
1008     async_rpc_send_reply(req, &result);
1009 }
1010
1011 void nfsproc3_fsstat_3_svc(nfs_fh3 *argp, RPCRequest *req)
1012 {
1013     fsstat3res result;
1014     memset(&result, 0, sizeof(result));
1015
1016     BlueSkyInode *inode = lookup_fh(req, argp);
1017     if (inode == NULL) {
1018         result.status = NFS3ERR_STALE;
1019         result.fsstat3res_u.resfail.present = FALSE;
1020         async_rpc_send_reply(req, &result);
1021         return;
1022     }
1023     g_mutex_lock(inode->lock);
1024
1025     result.status = NFS3_OK;
1026     result.fsstat3res_u.resok.obj_attributes.present = TRUE;
1027     encode_fattr3(&result.fsstat3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
1028
1029     result.fsstat3res_u.resok.tbytes = (1 << 30);
1030     result.fsstat3res_u.resok.fbytes = (1 << 30);
1031     result.fsstat3res_u.resok.abytes = (1 << 30);
1032     result.fsstat3res_u.resok.tfiles = 0;
1033     result.fsstat3res_u.resok.ffiles = 0;
1034     result.fsstat3res_u.resok.afiles = 0;
1035     result.fsstat3res_u.resok.invarsec = 0;
1036
1037     g_mutex_unlock(inode->lock);
1038     async_rpc_send_reply(req, &result);
1039 }
1040
1041 void nfsproc3_fsinfo_3_svc(nfs_fh3 *argp, RPCRequest *req)
1042 {
1043     fsinfo3res result;
1044     memset(&result, 0, sizeof(result));
1045
1046     BlueSkyInode *inode = bluesky_get_inode(fs, 1);
1047     g_mutex_lock(inode->lock);
1048     result.status = NFS3_OK;
1049     result.fsinfo3res_u.resok.obj_attributes.present = TRUE;
1050     encode_fattr3(&result.fsinfo3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
1051     result.fsinfo3res_u.resok.rtmax = 32768;
1052     result.fsinfo3res_u.resok.rtpref = 32768;
1053     result.fsinfo3res_u.resok.rtmult = 4096;
1054     result.fsinfo3res_u.resok.wtmax = 32768;
1055     result.fsinfo3res_u.resok.wtpref = 32768;
1056     result.fsinfo3res_u.resok.wtmult = 4096;
1057     result.fsinfo3res_u.resok.dtpref = 4096;
1058     result.fsinfo3res_u.resok.maxfilesize = 0x7fffffffffffffffULL;
1059     result.fsinfo3res_u.resok.time_delta.seconds = 0;
1060     result.fsinfo3res_u.resok.time_delta.nseconds = 1000;
1061     result.fsinfo3res_u.resok.properties
1062         = FSF3_LINK | FSF3_SYMLINK | FSF3_HOMOGENEOUS | FSF3_CANSETTIME;
1063
1064     g_mutex_unlock(inode->lock);
1065     bluesky_inode_unref(inode);
1066     async_rpc_send_reply(req, &result);
1067 }
1068
1069 void nfsproc3_pathconf_3_svc(nfs_fh3 *argp, RPCRequest *req)
1070 {
1071     pathconf3res result;
1072     memset(&result, 0, sizeof(result));
1073
1074     BlueSkyInode *inode = bluesky_get_inode(fs, 1);
1075     g_mutex_lock(inode->lock);
1076     result.status = NFS3_OK;
1077     result.pathconf3res_u.resok.obj_attributes.present = TRUE;
1078     encode_fattr3(&result.pathconf3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
1079     result.pathconf3res_u.resok.linkmax = 0xffffffff;
1080     result.pathconf3res_u.resok.name_max = 255;
1081     result.pathconf3res_u.resok.no_trunc = TRUE;
1082     result.pathconf3res_u.resok.chown_restricted = TRUE;
1083     result.pathconf3res_u.resok.case_insensitive = FALSE;
1084     result.pathconf3res_u.resok.case_preserving = TRUE;
1085
1086     g_mutex_unlock(inode->lock);
1087     bluesky_inode_unref(inode);
1088     async_rpc_send_reply(req, &result);
1089 }
1090
1091 void nfsproc3_commit_3_svc(commit3args *argp, RPCRequest *req)
1092 {
1093     commit3res result;
1094     memset(&result, 0, sizeof(result));
1095
1096     result.status = NFS3ERR_NOTSUPP;
1097
1098     BlueSkyInode *inode = lookup_fh(req, &argp->file);
1099     if (inode == NULL) {
1100         result.status = NFS3ERR_STALE;
1101         async_rpc_send_reply(req, &result);
1102         return;
1103     }
1104
1105     g_mutex_lock(inode->lock);
1106     encode_pre_wcc(&result.commit3res_u.resok.file_wcc, inode);
1107
1108     bluesky_inode_do_sync(inode);
1109
1110     result.commit3res_u.resok.file_wcc.after.present = TRUE;
1111     encode_fattr3(&result.commit3res_u.resok.file_wcc.after.post_op_attr_u.attributes, inode);
1112
1113     g_mutex_unlock(inode->lock);
1114
1115     async_rpc_send_reply(req, &result);
1116 }