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