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