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