11
#include <sys/types.h>
13
#include <sys/statvfs.h>
14
#include <sys/mount.h>
17
#include "fs-backend.h"
20
/* For debugging only */
25
#define BUFFER_SIZE 1024
27
static int check_export_path(const char *export_path, const char *path)
30
if (!export_path || !path)
32
if (strlen(path) < strlen(export_path))
34
if (strstr(path, "..") != NULL)
36
for (i = 0; i < strlen(path); i++) {
37
if (!isascii(path[i]))
40
if (strncmp(export_path, path, strlen(export_path)))
46
static unsigned short get_request(struct fs_mount *mount, struct fsif_request *req)
48
unsigned short id = get_id_from_freelist(mount->freelist);
50
FS_DEBUG("Private Request id: %d\n", id);
51
memcpy(&mount->requests[id].req_shadow, req, sizeof(struct fsif_request));
52
mount->requests[id].active = 1;
57
static int get_fd(struct fs_mount *mount)
61
for (i = 0; i < MAX_FDS; i++)
62
if (mount->fds[i] == -1)
68
static void dispatch_file_open(struct fs_mount *mount, struct fsif_request *req)
76
FS_DEBUG("Dispatching file open operation (gref=%d).\n", req->u.fopen.gref);
77
/* Read the request, and open file */
78
file_name = xc_gnttab_map_grant_ref(mount->gnth,
84
FS_DEBUG("File open issued for %s\n", file_name);
85
if (check_export_path(mount->export->export_path, file_name) < 0) {
86
FS_DEBUG("Filename check failed\n");
92
int real_fd = open(file_name, O_RDWR);
97
mount->fds[fd] = real_fd;
98
FS_DEBUG("Got FD: %d for real %d\n", fd, real_fd);
102
if (xc_gnttab_munmap(mount->gnth, file_name, 1) != 0) {
103
FS_DEBUG("ERROR: xc_gnttab_munmap failed errno=%d\n", errno);
104
terminate_mount_request(mount);
106
/* We can advance the request consumer index, from here on, the request
107
* should not be used (it may be overrinden by a response) */
108
mount->ring.req_cons++;
111
/* Get a response from the ring */
112
rsp_idx = mount->ring.rsp_prod_pvt++;
113
FS_DEBUG("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id);
114
rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx);
116
rsp->u.ret_val = (uint64_t)fd;
119
static void dispatch_file_close(struct fs_mount *mount, struct fsif_request *req)
123
fsif_response_t *rsp;
126
FS_DEBUG("Dispatching file close operation (fd=%d).\n", req->u.fclose.fd);
129
if (req->u.fclose.fd < MAX_FDS) {
130
int fd = mount->fds[req->u.fclose.fd];
132
mount->fds[req->u.fclose.fd] = -1;
135
FS_DEBUG("Got ret: %d\n", ret);
136
/* We can advance the request consumer index, from here on, the request
137
* should not be used (it may be overrinden by a response) */
138
mount->ring.req_cons++;
141
/* Get a response from the ring */
142
rsp_idx = mount->ring.rsp_prod_pvt++;
143
FS_DEBUG("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id);
144
rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx);
146
rsp->u.ret_val = (uint64_t)ret;
150
static void dispatch_file_read(struct fs_mount *mount, struct fsif_request *req)
155
unsigned short priv_id;
156
struct fs_request *priv_req;
158
/* Read the request */
159
assert(req->u.fread.len > 0);
160
count = (req->u.fread.len - 1) / XC_PAGE_SIZE + 1;
161
assert(count <= FSIF_NR_READ_GNTS);
162
buf = xc_gnttab_map_domain_grant_refs(mount->gnth,
169
FS_DEBUG("File read issued for FD=%d (len=%"PRIu64", offest=%"PRIu64")\n",
170
req->u.fread.fd, req->u.fread.len, req->u.fread.offset);
172
if (req->u.fread.fd < MAX_FDS)
173
fd = mount->fds[req->u.fread.fd];
177
priv_id = get_request(mount, req);
178
FS_DEBUG("Private id is: %d\n", priv_id);
179
priv_req = &mount->requests[priv_id];
180
priv_req->page = buf;
181
priv_req->count = count;
182
priv_req->id = priv_id;
184
/* Dispatch AIO read request */
185
bzero(&priv_req->aiocb, sizeof(struct aiocb));
186
priv_req->aiocb.aio_fildes = fd;
187
priv_req->aiocb.aio_nbytes = req->u.fread.len;
188
priv_req->aiocb.aio_offset = req->u.fread.offset;
189
priv_req->aiocb.aio_buf = buf;
190
priv_req->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
191
priv_req->aiocb.aio_sigevent.sigev_signo = SIGUSR2;
192
priv_req->aiocb.aio_sigevent.sigev_value.sival_ptr = priv_req;
193
if (aio_read(&priv_req->aiocb) < 0) {
194
FS_DEBUG("ERROR: aio_read failed errno=%d\n", errno);
195
xc_gnttab_munmap(mount->gnth, priv_req->page, priv_req->count);
196
terminate_mount_request(mount);
199
/* We can advance the request consumer index, from here on, the request
200
* should not be used (it may be overrinden by a response) */
201
mount->ring.req_cons++;
204
static void end_file_read(struct fs_mount *mount, struct fs_request *priv_req)
207
fsif_response_t *rsp;
210
/* Release the grant */
211
if (xc_gnttab_munmap(mount->gnth, priv_req->page, priv_req->count) != 0) {
212
FS_DEBUG("ERROR: xc_gnttab_munmap failed errno=%d\n", errno);
213
terminate_mount_request(mount);
216
/* Get a response from the ring */
217
rsp_idx = mount->ring.rsp_prod_pvt++;
218
req_id = priv_req->req_shadow.id;
219
FS_DEBUG("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id);
220
rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx);
222
rsp->u.ret_val = (uint64_t)aio_return(&priv_req->aiocb);
225
static void dispatch_file_write(struct fs_mount *mount, struct fsif_request *req)
230
unsigned short priv_id;
231
struct fs_request *priv_req;
233
/* Read the request */
234
assert(req->u.fwrite.len > 0);
235
count = (req->u.fwrite.len - 1) / XC_PAGE_SIZE + 1;
236
assert(count <= FSIF_NR_WRITE_GNTS);
237
buf = xc_gnttab_map_domain_grant_refs(mount->gnth,
244
FS_DEBUG("File write issued for FD=%d (len=%"PRIu64", offest=%"PRIu64")\n",
245
req->u.fwrite.fd, req->u.fwrite.len, req->u.fwrite.offset);
247
if (req->u.fwrite.fd < MAX_FDS)
248
fd = mount->fds[req->u.fwrite.fd];
252
priv_id = get_request(mount, req);
253
FS_DEBUG("Private id is: %d\n", priv_id);
254
priv_req = &mount->requests[priv_id];
255
priv_req->page = buf;
256
priv_req->count = count;
257
priv_req->id = priv_id;
259
/* Dispatch AIO write request */
260
bzero(&priv_req->aiocb, sizeof(struct aiocb));
261
priv_req->aiocb.aio_fildes = fd;
262
priv_req->aiocb.aio_nbytes = req->u.fwrite.len;
263
priv_req->aiocb.aio_offset = req->u.fwrite.offset;
264
priv_req->aiocb.aio_buf = buf;
265
priv_req->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
266
priv_req->aiocb.aio_sigevent.sigev_signo = SIGUSR2;
267
priv_req->aiocb.aio_sigevent.sigev_value.sival_ptr = priv_req;
268
if (aio_write(&priv_req->aiocb) < 0) {
269
FS_DEBUG("ERROR: aio_write failed errno=%d\n", errno);
270
xc_gnttab_munmap(mount->gnth, priv_req->page, priv_req->count);
271
terminate_mount_request(mount);
275
/* We can advance the request consumer index, from here on, the request
276
* should not be used (it may be overrinden by a response) */
277
mount->ring.req_cons++;
280
static void end_file_write(struct fs_mount *mount, struct fs_request *priv_req)
283
fsif_response_t *rsp;
286
/* Release the grant */
287
if (xc_gnttab_munmap(mount->gnth, priv_req->page, priv_req->count) != 0) {
288
FS_DEBUG("ERROR: xc_gnttab_munmap failed errno=%d\n", errno);
289
terminate_mount_request(mount);
292
/* Get a response from the ring */
293
rsp_idx = mount->ring.rsp_prod_pvt++;
294
req_id = priv_req->req_shadow.id;
295
FS_DEBUG("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id);
296
rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx);
298
rsp->u.ret_val = (uint64_t)aio_return(&priv_req->aiocb);
301
static void dispatch_stat(struct fs_mount *mount, struct fsif_request *req)
307
fsif_response_t *rsp;
310
if (req->u.fstat.fd < MAX_FDS)
311
fd = mount->fds[req->u.fstat.fd];
315
FS_DEBUG("File stat issued for FD=%d\n", req->u.fstat.fd);
317
/* We can advance the request consumer index, from here on, the request
318
* should not be used (it may be overrinden by a response) */
319
mount->ring.req_cons++;
321
/* Stat, and create the response */
322
ret = fstat(fd, &stat);
323
FS_DEBUG("Mode=%o, uid=%d, a_time=%ld\n",
324
stat.st_mode, stat.st_uid, (long)stat.st_atime);
326
/* Get a response from the ring */
327
rsp_idx = mount->ring.rsp_prod_pvt++;
328
FS_DEBUG("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id);
329
rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx);
331
rsp->u.fstat.stat_ret = (uint32_t)ret;
332
rsp->u.fstat.stat_mode = stat.st_mode;
333
rsp->u.fstat.stat_uid = stat.st_uid;
334
rsp->u.fstat.stat_gid = stat.st_gid;
336
if (S_ISBLK(stat.st_mode)) {
337
unsigned long sectors;
338
if (ioctl(fd, BLKGETSIZE, §ors)) {
339
perror("getting device size\n");
340
rsp->u.fstat.stat_size = 0;
342
rsp->u.fstat.stat_size = sectors << 9;
345
rsp->u.fstat.stat_size = stat.st_size;
346
rsp->u.fstat.stat_atime = stat.st_atime;
347
rsp->u.fstat.stat_mtime = stat.st_mtime;
348
rsp->u.fstat.stat_ctime = stat.st_ctime;
352
static void dispatch_truncate(struct fs_mount *mount, struct fsif_request *req)
357
fsif_response_t *rsp;
361
length = req->u.ftruncate.length;
362
FS_DEBUG("File truncate issued for FD=%d, length=%"PRId64"\n", req->u.ftruncate.fd, length);
364
if (req->u.ftruncate.fd < MAX_FDS)
365
fd = mount->fds[req->u.ftruncate.fd];
369
/* We can advance the request consumer index, from here on, the request
370
* should not be used (it may be overrinden by a response) */
371
mount->ring.req_cons++;
373
/* Stat, and create the response */
374
ret = ftruncate(fd, length);
376
/* Get a response from the ring */
377
rsp_idx = mount->ring.rsp_prod_pvt++;
378
FS_DEBUG("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id);
379
rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx);
381
rsp->u.ret_val = (uint64_t)ret;
384
static void dispatch_remove(struct fs_mount *mount, struct fsif_request *req)
389
fsif_response_t *rsp;
392
FS_DEBUG("Dispatching remove operation (gref=%d).\n", req->u.fremove.gref);
393
/* Read the request, and open file */
394
file_name = xc_gnttab_map_grant_ref(mount->gnth,
400
FS_DEBUG("File remove issued for %s\n", file_name);
401
if (check_export_path(mount->export->export_path, file_name) < 0) {
402
FS_DEBUG("Filename check failed\n");
405
ret = remove(file_name);
407
FS_DEBUG("Got ret: %d\n", ret);
408
if (xc_gnttab_munmap(mount->gnth, file_name, 1) != 0) {
409
FS_DEBUG("ERROR: xc_gnttab_munmap failed errno=%d\n", errno);
410
terminate_mount_request(mount);
412
/* We can advance the request consumer index, from here on, the request
413
* should not be used (it may be overrinden by a response) */
414
mount->ring.req_cons++;
417
/* Get a response from the ring */
418
rsp_idx = mount->ring.rsp_prod_pvt++;
419
FS_DEBUG("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id);
420
rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx);
422
rsp->u.ret_val = (uint64_t)ret;
426
static void dispatch_rename(struct fs_mount *mount, struct fsif_request *req)
428
char *buf, *old_file_name, *new_file_name;
431
fsif_response_t *rsp;
434
FS_DEBUG("Dispatching rename operation (gref=%d).\n", req->u.fremove.gref);
435
/* Read the request, and open file */
436
buf = xc_gnttab_map_grant_ref(mount->gnth,
442
old_file_name = buf + req->u.frename.old_name_offset;
443
new_file_name = buf + req->u.frename.new_name_offset;
444
FS_DEBUG("File rename issued for %s -> %s (buf=%s)\n",
445
old_file_name, new_file_name, buf);
446
if (check_export_path(mount->export->export_path, old_file_name) < 0 ||
447
check_export_path(mount->export->export_path, new_file_name) < 0) {
448
FS_DEBUG("Filename check failed\n");
451
ret = rename(old_file_name, new_file_name);
453
FS_DEBUG("Got ret: %d\n", ret);
454
if (xc_gnttab_munmap(mount->gnth, buf, 1) != 0) {
455
FS_DEBUG("ERROR: xc_gnttab_munmap failed errno=%d\n", errno);
456
terminate_mount_request(mount);
458
/* We can advance the request consumer index, from here on, the request
459
* should not be used (it may be overrinden by a response) */
460
mount->ring.req_cons++;
463
/* Get a response from the ring */
464
rsp_idx = mount->ring.rsp_prod_pvt++;
465
FS_DEBUG("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id);
466
rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx);
468
rsp->u.ret_val = (uint64_t)ret;
472
static void dispatch_create(struct fs_mount *mount, struct fsif_request *req)
479
fsif_response_t *rsp;
482
FS_DEBUG("Dispatching file create operation (gref=%d).\n", req->u.fcreate.gref);
483
/* Read the request, and create file/directory */
484
mode = req->u.fcreate.mode;
485
directory = req->u.fcreate.directory;
486
file_name = xc_gnttab_map_grant_ref(mount->gnth,
492
if (check_export_path(mount->export->export_path, file_name) < 0) {
493
FS_DEBUG("Filename check failed\n");
497
/* We can advance the request consumer index, from here on, the request
498
* should not be used (it may be overrinden by a response) */
499
mount->ring.req_cons++;
503
FS_DEBUG("Issuing create for directory: %s\n", file_name);
504
ret = mkdir(file_name, mode);
508
FS_DEBUG("Issuing create for file: %s\n", file_name);
511
int real_fd = creat(file_name, mode);
516
mount->fds[ret] = real_fd;
517
FS_DEBUG("Got FD: %d for real %d\n", ret, real_fd);
522
if (xc_gnttab_munmap(mount->gnth, file_name, 1) != 0) {
523
FS_DEBUG("ERROR: xc_gnttab_munmap failed errno=%d\n", errno);
524
terminate_mount_request(mount);
526
FS_DEBUG("Got ret %d (errno=%d)\n", ret, errno);
528
/* Get a response from the ring */
529
rsp_idx = mount->ring.rsp_prod_pvt++;
530
FS_DEBUG("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id);
531
rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx);
533
rsp->u.ret_val = (uint64_t)ret;
536
static void dispatch_list(struct fs_mount *mount, struct fsif_request *req)
538
char *file_name, *buf;
539
uint32_t offset = 0, nr_files = 0, error_code = 0;
542
fsif_response_t *rsp;
545
struct dirent *dirent = NULL;
547
FS_DEBUG("Dispatching list operation (gref=%d).\n", req->u.flist.gref);
548
/* Read the request, and list directory */
549
offset = req->u.flist.offset;
550
buf = file_name = xc_gnttab_map_grant_ref(mount->gnth,
553
PROT_READ | PROT_WRITE);
556
FS_DEBUG("Dir list issued for %s\n", file_name);
557
if (check_export_path(mount->export->export_path, file_name) < 0) {
558
FS_DEBUG("Filename check failed\n");
562
/* We can advance the request consumer index, from here on, the request
563
* should not be used (it may be overrinden by a response) */
564
mount->ring.req_cons++;
568
dir = opendir(file_name);
574
/* Skip offset dirs */
575
dirent = readdir(dir);
576
while(offset-- > 0 && dirent != NULL)
577
dirent = readdir(dir);
578
/* If there was any error with reading the directory, errno will be set */
580
/* Copy file names of the remaining non-NULL dirents into buf */
581
if (NAME_MAX >= XC_PAGE_SIZE >> 1)
583
while(dirent != NULL &&
584
(XC_PAGE_SIZE - ((unsigned long)buf & XC_PAGE_MASK) > NAME_MAX))
586
int curr_length = strlen(dirent->d_name) + 1;
588
memcpy(buf, dirent->d_name, curr_length);
590
dirent = readdir(dir);
595
ret_val = ((nr_files << NR_FILES_SHIFT) & NR_FILES_MASK) |
596
((error_code << ERROR_SHIFT) & ERROR_MASK) |
597
(dirent != NULL ? HAS_MORE_FLAG : 0);
598
if (xc_gnttab_munmap(mount->gnth, file_name, 1) != 0) {
599
FS_DEBUG("ERROR: xc_gnttab_munmap failed errno=%d\n", errno);
600
terminate_mount_request(mount);
603
/* Get a response from the ring */
604
rsp_idx = mount->ring.rsp_prod_pvt++;
605
FS_DEBUG("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id);
606
rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx);
608
rsp->u.ret_val = ret_val;
611
static void dispatch_chmod(struct fs_mount *mount, struct fsif_request *req)
615
fsif_response_t *rsp;
619
FS_DEBUG("Dispatching file chmod operation (fd=%d, mode=%o).\n",
620
req->u.fchmod.fd, req->u.fchmod.mode);
622
if (req->u.fchmod.fd < MAX_FDS)
623
fd = mount->fds[req->u.fchmod.fd];
627
mode = req->u.fchmod.mode;
628
/* We can advance the request consumer index, from here on, the request
629
* should not be used (it may be overrinden by a response) */
630
mount->ring.req_cons++;
632
ret = fchmod(fd, mode);
634
/* Get a response from the ring */
635
rsp_idx = mount->ring.rsp_prod_pvt++;
636
FS_DEBUG("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id);
637
rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx);
639
rsp->u.ret_val = (uint64_t)ret;
642
static void dispatch_fs_space(struct fs_mount *mount, struct fsif_request *req)
646
fsif_response_t *rsp;
651
FS_DEBUG("Dispatching fs space operation (gref=%d).\n", req->u.fspace.gref);
652
/* Read the request, and open file */
653
file_name = xc_gnttab_map_grant_ref(mount->gnth,
659
FS_DEBUG("Fs space issued for %s\n", file_name);
660
if (check_export_path(mount->export->export_path, file_name) < 0) {
661
FS_DEBUG("Filename check failed\n");
664
ret = statvfs(file_name, &stat);
667
ret = stat.f_bsize * stat.f_bfree;
669
if (xc_gnttab_munmap(mount->gnth, file_name, 1) != 0) {
670
FS_DEBUG("ERROR: xc_gnttab_munmap failed errno=%d\n", errno);
671
terminate_mount_request(mount);
673
/* We can advance the request consumer index, from here on, the request
674
* should not be used (it may be overrinden by a response) */
675
mount->ring.req_cons++;
678
/* Get a response from the ring */
679
rsp_idx = mount->ring.rsp_prod_pvt++;
680
FS_DEBUG("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id);
681
rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx);
683
rsp->u.ret_val = (uint64_t)ret;
686
static void dispatch_file_sync(struct fs_mount *mount, struct fsif_request *req)
690
unsigned short priv_id;
691
struct fs_request *priv_req;
694
if (req->u.fsync.fd < MAX_FDS)
695
fd = mount->fds[req->u.fsync.fd];
699
FS_DEBUG("File sync issued for FD=%d\n", req->u.fsync.fd);
701
priv_id = get_request(mount, req);
702
FS_DEBUG("Private id is: %d\n", priv_id);
703
priv_req = &mount->requests[priv_id];
704
priv_req->id = priv_id;
706
/* Dispatch AIO read request */
707
bzero(&priv_req->aiocb, sizeof(struct aiocb));
708
priv_req->aiocb.aio_fildes = fd;
709
priv_req->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
710
priv_req->aiocb.aio_sigevent.sigev_signo = SIGUSR2;
711
priv_req->aiocb.aio_sigevent.sigev_value.sival_ptr = priv_req;
712
if (aio_fsync(O_SYNC, &priv_req->aiocb) < 0) {
713
FS_DEBUG("ERROR: aio_fsync failed errno=%d\n", errno);
714
terminate_mount_request(mount);
717
/* We can advance the request consumer index, from here on, the request
718
* should not be used (it may be overrinden by a response) */
719
mount->ring.req_cons++;
722
static void end_file_sync(struct fs_mount *mount, struct fs_request *priv_req)
725
fsif_response_t *rsp;
728
/* Get a response from the ring */
729
rsp_idx = mount->ring.rsp_prod_pvt++;
730
req_id = priv_req->req_shadow.id;
731
FS_DEBUG("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id);
732
rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx);
734
rsp->u.ret_val = (uint64_t)aio_return(&priv_req->aiocb);
737
struct fs_op fopen_op = {.type = REQ_FILE_OPEN,
738
.dispatch_handler = dispatch_file_open,
739
.response_handler = NULL};
740
struct fs_op fclose_op = {.type = REQ_FILE_CLOSE,
741
.dispatch_handler = dispatch_file_close,
742
.response_handler = NULL};
743
struct fs_op fread_op = {.type = REQ_FILE_READ,
744
.dispatch_handler = dispatch_file_read,
745
.response_handler = end_file_read};
746
struct fs_op fwrite_op = {.type = REQ_FILE_WRITE,
747
.dispatch_handler = dispatch_file_write,
748
.response_handler = end_file_write};
749
struct fs_op fstat_op = {.type = REQ_STAT,
750
.dispatch_handler = dispatch_stat,
751
.response_handler = NULL};
752
struct fs_op ftruncate_op = {.type = REQ_FILE_TRUNCATE,
753
.dispatch_handler = dispatch_truncate,
754
.response_handler = NULL};
755
struct fs_op fremove_op = {.type = REQ_REMOVE,
756
.dispatch_handler = dispatch_remove,
757
.response_handler = NULL};
758
struct fs_op frename_op = {.type = REQ_RENAME,
759
.dispatch_handler = dispatch_rename,
760
.response_handler = NULL};
761
struct fs_op fcreate_op = {.type = REQ_CREATE,
762
.dispatch_handler = dispatch_create,
763
.response_handler = NULL};
764
struct fs_op flist_op = {.type = REQ_DIR_LIST,
765
.dispatch_handler = dispatch_list,
766
.response_handler = NULL};
767
struct fs_op fchmod_op = {.type = REQ_CHMOD,
768
.dispatch_handler = dispatch_chmod,
769
.response_handler = NULL};
770
struct fs_op fspace_op = {.type = REQ_FS_SPACE,
771
.dispatch_handler = dispatch_fs_space,
772
.response_handler = NULL};
773
struct fs_op fsync_op = {.type = REQ_FILE_SYNC,
774
.dispatch_handler = dispatch_file_sync,
775
.response_handler = end_file_sync};
778
struct fs_op *fsops[] = {&fopen_op,