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