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