NFSv3 SETATTR operation.
[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 static int null_int;
13 static void *null_result = (void *)&null_int;
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 te 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 /* Look up a BlueSkyInode given an NFS filehandle.  Returns NULL if the
34  * filehandle is invalid. */
35 BlueSkyInode *lookup_fh(nfs_fh3 *fh)
36 {
37     BlueSkyInode *inode = NULL;
38     if (fh->data.data_len == 8) {
39         uint64_t inum = GUINT64_FROM_BE(*(uint64_t *)(fh->data.data_val));
40         inode = bluesky_get_inode(fs, inum);
41     }
42     return inode;
43 }
44
45 int64_t decode_nfstime3(nfstime3 *time)
46 {
47     int64_t result = (int64_t)time->seconds * 1000000;
48     result += time->nseconds / 1000;
49     return result;
50 }
51
52 /* Copy inode attributes into NFS response.  The BlueSkyInode should be locked
53  * by the caller. */
54 void encode_fattr3(struct fattr3 *result, BlueSkyInode *inode)
55 {
56     result->type = inode->type;
57     result->mode = inode->mode;
58     result->nlink = inode->nlink;
59     result->uid = inode->uid;
60     result->gid = inode->gid;
61     result->size = inode->size;
62     result->used = 0;
63     result->rdev.major = 0;
64     result->rdev.minor = 0;
65     result->fsid = 0;
66     result->fileid = inode->inum;
67     result->atime.seconds = inode->atime / 1000000;
68     result->atime.nseconds = (inode->atime % 1000000) * 1000;
69     result->mtime.seconds = inode->mtime / 1000000;
70     result->mtime.nseconds = (inode->mtime % 1000000) * 1000;
71     result->ctime.seconds = inode->ctime / 1000000;
72     result->ctime.nseconds = (inode->ctime % 1000000) * 1000;
73 }
74
75 void encode_pre_wcc(struct wcc_data *wcc, BlueSkyInode *inode)
76 {
77     wcc->before.present = TRUE;
78     wcc->before.pre_op_attr_u.attributes.size = inode->size;
79     wcc->before.pre_op_attr_u.attributes.mtime.seconds = inode->mtime / 1000000;
80     wcc->before.pre_op_attr_u.attributes.mtime.nseconds = (inode->mtime % 1000000) * 1000;
81     wcc->before.pre_op_attr_u.attributes.ctime.seconds = inode->ctime / 1000000;
82     wcc->before.pre_op_attr_u.attributes.ctime.nseconds = (inode->ctime % 1000000) * 1000;
83 }
84 void *
85 nfsproc3_null_3_svc(void *argp, struct svc_req *rqstp)
86 {
87     return null_result;
88 }
89
90 getattr3res *
91 nfsproc3_getattr_3_svc(nfs_fh3 *argp, struct svc_req *rqstp)
92 {
93     static getattr3res result;
94
95     BlueSkyInode *inode = lookup_fh(argp);
96     if (inode != NULL) {
97         result.status = NFS3_OK;
98         encode_fattr3(&result.getattr3res_u.attributes, inode);
99     } else {
100         result.status = NFS3ERR_STALE;
101     }
102
103     return &result;
104 }
105
106 wccstat3 *
107 nfsproc3_setattr_3_svc(setattr3args *argp, struct svc_req *rqstp)
108 {
109     static wccstat3 result;
110
111     result.wccstat3_u.wcc.before.present = FALSE;
112     result.wccstat3_u.wcc.after.present = FALSE;
113     BlueSkyInode *inode = lookup_fh(&argp->object);
114     if (inode == NULL) {
115         result.status = NFS3ERR_STALE;
116         return &result;
117     }
118
119     encode_pre_wcc(&result.wccstat3_u.wcc, inode);
120     if (argp->guard.check) {
121         if (inode->ctime != decode_nfstime3(&argp->guard.sattrguard3_u.ctime)) {
122             result.status = NFS3ERR_NOT_SYNC;
123             result.wccstat3_u.wcc.after.present = TRUE;
124             encode_fattr3(&result.wccstat3_u.wcc.after.post_op_attr_u.attributes, inode);
125             return &result;
126         }
127     }
128
129     if (argp->new_attributes.mode.set) {
130         inode->mode = argp->new_attributes.mode.set_uint32_u.val;
131     }
132
133     if (argp->new_attributes.uid.set) {
134         inode->uid = argp->new_attributes.uid.set_uint32_u.val;
135     }
136
137     if (argp->new_attributes.gid.set) {
138         inode->gid = argp->new_attributes.gid.set_uint32_u.val;
139     }
140
141     int64_t now = bluesky_get_current_time();
142
143     if (argp->new_attributes.size.set) {
144         bluesky_file_truncate(inode,
145                               argp->new_attributes.size.set_uint64_u.val);
146         inode->mtime = now;
147     }
148
149     switch (argp->new_attributes.atime.set) {
150     case DONT_CHANGE:
151         break;
152     case SET_TO_SERVER_TIME:
153         inode->atime = now;
154         break;
155     case SET_TO_CLIENT_TIME:
156         inode->atime = decode_nfstime3(&argp->new_attributes.atime.set_time_u.time);
157         break;
158     }
159
160     switch (argp->new_attributes.mtime.set) {
161     case DONT_CHANGE:
162         break;
163     case SET_TO_SERVER_TIME:
164         inode->mtime = now;
165         break;
166     case SET_TO_CLIENT_TIME:
167         inode->mtime = decode_nfstime3(&argp->new_attributes.mtime.set_time_u.time);
168         break;
169     }
170
171     inode->ctime = now;
172     inode->change_count++;
173
174     result.wccstat3_u.wcc.after.present = TRUE;
175     encode_fattr3(&result.wccstat3_u.wcc.after.post_op_attr_u.attributes,
176                   inode);
177     result.status = NFS3_OK;
178
179     return &result;
180 }
181
182 lookup3res *
183 nfsproc3_lookup_3_svc(diropargs3 *argp, struct svc_req *rqstp)
184 {
185     static lookup3res result;
186
187     BlueSkyInode *dir = lookup_fh(&argp->dir);
188     if (dir == NULL) {
189         result.status = NFS3ERR_STALE;
190         result.lookup3res_u.resfail.present = FALSE;
191         return &result;
192     }
193
194     result.lookup3res_u.resfail.present = TRUE;
195     encode_fattr3(&result.lookup3res_u.resfail.post_op_attr_u.attributes, dir);
196     if (!validate_filename(argp->name)) {
197         if (strlen(argp->name) > 255)
198             result.status = NFS3ERR_NAMETOOLONG;
199         else
200             result.status = NFS3ERR_NOENT;
201         return &result;
202     }
203
204     /* TODO: Special-case "." and "..". */
205     uint64_t inum = bluesky_directory_lookup(dir, argp->name);
206     if (inum == 0) {
207         result.status = NFS3ERR_NOENT;
208         return &result;
209     }
210     BlueSkyInode *inode = bluesky_get_inode(fs, inum);
211     if (inode == NULL) {
212         result.status = NFS3ERR_NOENT;
213         return &result;
214     }
215
216     result.status = NFS3_OK;
217     result.lookup3res_u.resok.dir_attributes.present = TRUE;
218     encode_fattr3(&result.lookup3res_u.resok.dir_attributes.post_op_attr_u.attributes, dir);
219     result.lookup3res_u.resok.obj_attributes.present = TRUE;
220     encode_fattr3(&result.lookup3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
221
222     static uint64_t fh_bytes;
223     fh_bytes = GUINT64_TO_BE(inum);
224     result.lookup3res_u.resok.object.data.data_len = 8;
225     result.lookup3res_u.resok.object.data.data_val = (char *)&fh_bytes;
226
227     return &result;
228 }
229
230 access3res *
231 nfsproc3_access_3_svc(access3args *argp, struct svc_req *rqstp)
232 {
233     static access3res result;
234
235     BlueSkyInode *inode = lookup_fh(&argp->object);
236     if (inode == NULL) {
237         result.status = NFS3ERR_STALE;
238         result.access3res_u.resfail.present = FALSE;
239         return &result;
240     }
241
242     result.status = NFS3_OK;
243     result.access3res_u.resok.obj_attributes.present = TRUE;
244     encode_fattr3(&result.access3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
245     result.access3res_u.resok.access = argp->access;
246
247     return &result;
248 }
249
250 readlink3res *
251 nfsproc3_readlink_3_svc(nfs_fh3 *argp, struct svc_req *rqstp)
252 {
253     static readlink3res result;
254
255     result.status = NFS3ERR_NOTSUPP;
256
257     return &result;
258 }
259
260 read3res *
261 nfsproc3_read_3_svc(read3args *argp, struct svc_req *rqstp)
262 {
263     static read3res result;
264     static char buf[32768];
265
266     BlueSkyInode *inode = lookup_fh(&argp->file);
267     if (inode == NULL) {
268         result.status = NFS3ERR_STALE;
269         result.read3res_u.resfail.present = FALSE;
270         return &result;
271     }
272
273     int count = argp->count;
274     if (argp->offset >= inode->size) {
275         count = 0;
276         result.read3res_u.resok.eof = TRUE;
277     } else {
278         count = MIN(count, inode->size - argp->offset);
279         if (argp->offset + count == inode->size)
280             result.read3res_u.resok.eof = TRUE;
281         else
282             result.read3res_u.resok.eof = FALSE;
283
284         bluesky_file_read(inode, argp->offset, buf, count);
285     }
286
287     result.status = NFS3_OK;
288     result.read3res_u.resok.file_attributes.present = TRUE;
289     encode_fattr3(&result.read3res_u.resok.file_attributes.post_op_attr_u.attributes, inode);
290     result.read3res_u.resok.count = count;
291     result.read3res_u.resok.data.data_val = buf;
292     result.read3res_u.resok.data.data_len = count;
293
294     return &result;
295 }
296
297 write3res *
298 nfsproc3_write_3_svc(write3args *argp, struct svc_req *rqstp)
299 {
300     static write3res result;
301     struct wcc_data wcc;
302     memset(&wcc, 0, sizeof(wcc));
303
304     BlueSkyInode *inode = lookup_fh(&argp->file);
305     if (inode == NULL) {
306         result.status = NFS3ERR_STALE;
307         result.write3res_u.resfail = wcc;
308         return &result;
309     }
310
311     encode_pre_wcc(&wcc, inode);
312     if (inode->type != BLUESKY_REGULAR) {
313         result.status = NFS3ERR_INVAL;
314         result.write3res_u.resfail = wcc;
315         return &result;
316     }
317
318     uint64_t lastbyte = argp->offset + argp->count;
319     if (lastbyte > inode->size) {
320         bluesky_file_truncate(inode, lastbyte);
321     }
322
323     if (argp->data.data_len < argp->count) {
324         /* ??? */
325     } else {
326         bluesky_file_write(inode, argp->offset,
327                            argp->data.data_val, argp->count);
328     }
329
330     encode_fattr3(&wcc.after.post_op_attr_u.attributes, inode);
331     result.write3res_u.resok.file_wcc = wcc;
332     result.write3res_u.resok.count = argp->count;
333     result.write3res_u.resok.committed = FILE_SYNC;
334
335     return &result;
336 }
337
338 diropres3 *
339 nfsproc3_create_3_svc(create3args *argp, struct svc_req *rqstp)
340 {
341     static diropres3 result;
342     struct wcc_data wcc;
343     memset(&wcc, 0, sizeof(wcc));
344
345     BlueSkyInode *dir = lookup_fh(&argp->where.dir);
346     if (dir == NULL) {
347         result.status = NFS3ERR_STALE;
348         result.diropres3_u.resfail = wcc;
349         return &result;
350     }
351
352     encode_pre_wcc(&wcc, dir);
353     if (dir->type != BLUESKY_DIRECTORY) {
354         result.status = NFS3ERR_NOTDIR;
355         result.diropres3_u.resfail = wcc;
356         return &result;
357     }
358
359     if (!validate_filename(argp->where.name)
360         || strcmp(argp->where.name, ".") == 0
361         || strcmp(argp->where.name, "..") == 0)
362     {
363         result.status = NFS3ERR_EXIST;
364         result.diropres3_u.resfail = wcc;
365         return &result;
366     }
367
368     BlueSkyInode *file;
369     file = bluesky_new_inode(bluesky_fs_alloc_inode(fs), fs, BLUESKY_REGULAR);
370     file->nlink = 1;
371     file->mode = 0755;
372     int64_t time = bluesky_get_current_time();
373     printf("time: %lld\n", time);
374     file->mtime = time;
375     file->ctime = time;
376     file->atime = time;
377     bluesky_insert_inode(fs, file);
378     bluesky_directory_insert(dir, argp->where.name, file->inum);
379
380     wcc.after.present = TRUE;
381     encode_fattr3(&wcc.after.post_op_attr_u.attributes, dir);
382     result.diropres3_u.resok.obj_attributes.present = TRUE;
383     encode_fattr3(&result.diropres3_u.resok.obj_attributes.post_op_attr_u.attributes, file);
384     result.diropres3_u.resok.dir_wcc = wcc;
385
386     static uint64_t fh_bytes;
387     fh_bytes = GUINT64_TO_BE(file->inum);
388     result.diropres3_u.resok.obj.present = TRUE;
389     result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_len = 8;
390     result.diropres3_u.resok.obj.post_op_fh3_u.handle.data.data_val = (char *)&fh_bytes;
391
392     return &result;
393 }
394
395 diropres3 *
396 nfsproc3_mkdir_3_svc(mkdir3args *argp, struct svc_req *rqstp)
397 {
398     static diropres3 result;
399
400     result.status = NFS3ERR_NOTSUPP;
401
402     return &result;
403 }
404
405 diropres3 *
406 nfsproc3_symlink_3_svc(symlink3args *argp, struct svc_req *rqstp)
407 {
408     static diropres3 result;
409
410     result.status = NFS3ERR_NOTSUPP;
411
412     return &result;
413 }
414
415 diropres3 *
416 nfsproc3_mknod_3_svc(mknod3args *argp, struct svc_req *rqstp)
417 {
418     static diropres3 result;
419
420     result.status = NFS3ERR_NOTSUPP;
421
422     return &result;
423 }
424
425 wccstat3 *
426 nfsproc3_remove_3_svc(diropargs3 *argp, struct svc_req *rqstp)
427 {
428     static wccstat3 result;
429
430     result.status = NFS3ERR_NOTSUPP;
431
432     return &result;
433 }
434
435 wccstat3 *
436 nfsproc3_rmdir_3_svc(diropargs3 *argp, struct svc_req *rqstp)
437 {
438     static wccstat3 result;
439
440     result.status = NFS3ERR_NOTSUPP;
441
442     return &result;
443 }
444
445 rename3res *
446 nfsproc3_rename_3_svc(rename3args *argp, struct svc_req *rqstp)
447 {
448     static rename3res result;
449
450     result.status = NFS3ERR_NOTSUPP;
451
452     return &result;
453 }
454
455 link3res *
456 nfsproc3_link_3_svc(link3args *argp, struct svc_req *rqstp)
457 {
458     static link3res result;
459
460     result.status = NFS3ERR_NOTSUPP;
461
462     return &result;
463 }
464
465 gint bluesky_dirent_compare(gconstpointer a, gconstpointer b,
466                             gpointer unused);
467
468 readdir3res *
469 nfsproc3_readdir_3_svc(readdir3args *argp, struct svc_req *rqstp)
470 {
471     static readdir3res result;
472
473     BlueSkyInode *dir = lookup_fh(&argp->dir);
474     if (dir == NULL) {
475         result.status = NFS3ERR_STALE;
476         result.readdir3res_u.resfail.present = FALSE;
477         return &result;
478     }
479
480     result.status = NFS3_OK;
481     result.readdir3res_u.resok.dir_attributes.present = TRUE;
482     encode_fattr3(&result.readdir3res_u.resok.dir_attributes.post_op_attr_u.attributes, dir);
483     memset(result.readdir3res_u.resok.cookieverf, 0,
484            sizeof(result.readdir3res_u.resok.cookieverf));
485
486 #define MAX_READDIR_DIRENTS 4
487     static entry3 dirents[MAX_READDIR_DIRENTS];
488     int count = 0;
489
490     BlueSkyDirent start = {NULL, argp->cookie, 0};
491     GSequenceIter *i = g_sequence_search(dir->dirents, &start,
492                                          bluesky_dirent_compare, NULL);
493
494     while (count < MAX_READDIR_DIRENTS && !g_sequence_iter_is_end(i)) {
495         BlueSkyDirent *d = g_sequence_get(i);
496         dirents[count].fileid = d->inum;
497         dirents[count].name = d->name;
498         dirents[count].cookie = d->cookie;
499         dirents[count].nextentry = NULL;
500         if (count > 0)
501             dirents[count - 1].nextentry = &dirents[count];
502         i = g_sequence_iter_next(i);
503         count++;
504     }
505
506     if (count > 0)
507         result.readdir3res_u.resok.reply.entries = &dirents[0];
508     else
509         result.readdir3res_u.resok.reply.entries = NULL;
510     result.readdir3res_u.resok.reply.eof = g_sequence_iter_is_end(i);
511
512     return &result;
513 }
514
515 readdirplus3res *
516 nfsproc3_readdirplus_3_svc(readdirplus3args *argp, struct svc_req *rqstp)
517 {
518     static readdirplus3res result;
519
520     result.status = NFS3ERR_NOTSUPP;
521
522     return &result;
523 }
524
525 fsstat3res *
526 nfsproc3_fsstat_3_svc(nfs_fh3 *argp, struct svc_req *rqstp)
527 {
528     static fsstat3res result;
529
530     result.status = NFS3ERR_NOTSUPP;
531
532     return &result;
533 }
534
535 fsinfo3res *
536 nfsproc3_fsinfo_3_svc(nfs_fh3 *argp, struct svc_req *rqstp)
537 {
538     static fsinfo3res result;
539
540     BlueSkyInode *inode = bluesky_get_inode(fs, 1);
541     result.status = NFS3_OK;
542     result.fsinfo3res_u.resok.obj_attributes.present = TRUE;
543     encode_fattr3(&result.fsinfo3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
544     result.fsinfo3res_u.resok.rtmax = 32768;
545     result.fsinfo3res_u.resok.rtpref = 32768;
546     result.fsinfo3res_u.resok.rtmult = 4096;
547     result.fsinfo3res_u.resok.wtmax = 32768;
548     result.fsinfo3res_u.resok.wtpref = 32768;
549     result.fsinfo3res_u.resok.wtmult = 4096;
550     result.fsinfo3res_u.resok.dtpref = 4096;
551     result.fsinfo3res_u.resok.maxfilesize = 0x7fffffffffffffffULL;
552     result.fsinfo3res_u.resok.time_delta.seconds = 0;
553     result.fsinfo3res_u.resok.time_delta.nseconds = 1000;
554     result.fsinfo3res_u.resok.properties
555         = FSF3_LINK | FSF3_SYMLINK | FSF3_HOMOGENEOUS | FSF3_CANSETTIME;
556
557     return &result;
558 }
559
560 pathconf3res *
561 nfsproc3_pathconf_3_svc(nfs_fh3 *argp, struct svc_req *rqstp)
562 {
563     static pathconf3res result;
564
565     BlueSkyInode *inode = bluesky_get_inode(fs, 1);
566     result.status = NFS3_OK;
567     result.pathconf3res_u.resok.obj_attributes.present = TRUE;
568     encode_fattr3(&result.pathconf3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
569     result.pathconf3res_u.resok.linkmax = 0xffffffff;
570     result.pathconf3res_u.resok.name_max = 255;
571     result.pathconf3res_u.resok.no_trunc = TRUE;
572     result.pathconf3res_u.resok.chown_restricted = TRUE;
573     result.pathconf3res_u.resok.case_insensitive = FALSE;
574     result.pathconf3res_u.resok.case_preserving = TRUE;
575
576     return &result;
577 }
578
579 commit3res *
580 nfsproc3_commit_3_svc(commit3args *argp, struct svc_req *rqstp)
581 {
582     static commit3res result;
583
584     result.status = NFS3ERR_NOTSUPP;
585
586     return &result;
587 }