diff options
Diffstat (limited to 'fs/fuse/dev.c')
-rw-r--r-- | fs/fuse/dev.c | 175 |
1 files changed, 144 insertions, 31 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 4623018e104a..2795045484ee 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/pagemap.h> | 16 | #include <linux/pagemap.h> |
17 | #include <linux/file.h> | 17 | #include <linux/file.h> |
18 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
19 | #include <linux/pipe_fs_i.h> | ||
19 | 20 | ||
20 | MODULE_ALIAS_MISCDEV(FUSE_MINOR); | 21 | MODULE_ALIAS_MISCDEV(FUSE_MINOR); |
21 | 22 | ||
@@ -498,6 +499,9 @@ struct fuse_copy_state { | |||
498 | int write; | 499 | int write; |
499 | struct fuse_req *req; | 500 | struct fuse_req *req; |
500 | const struct iovec *iov; | 501 | const struct iovec *iov; |
502 | struct pipe_buffer *pipebufs; | ||
503 | struct pipe_buffer *currbuf; | ||
504 | struct pipe_inode_info *pipe; | ||
501 | unsigned long nr_segs; | 505 | unsigned long nr_segs; |
502 | unsigned long seglen; | 506 | unsigned long seglen; |
503 | unsigned long addr; | 507 | unsigned long addr; |
@@ -522,7 +526,14 @@ static void fuse_copy_init(struct fuse_copy_state *cs, struct fuse_conn *fc, | |||
522 | /* Unmap and put previous page of userspace buffer */ | 526 | /* Unmap and put previous page of userspace buffer */ |
523 | static void fuse_copy_finish(struct fuse_copy_state *cs) | 527 | static void fuse_copy_finish(struct fuse_copy_state *cs) |
524 | { | 528 | { |
525 | if (cs->mapaddr) { | 529 | if (cs->currbuf) { |
530 | struct pipe_buffer *buf = cs->currbuf; | ||
531 | |||
532 | buf->ops->unmap(cs->pipe, buf, cs->mapaddr); | ||
533 | |||
534 | cs->currbuf = NULL; | ||
535 | cs->mapaddr = NULL; | ||
536 | } else if (cs->mapaddr) { | ||
526 | kunmap_atomic(cs->mapaddr, KM_USER0); | 537 | kunmap_atomic(cs->mapaddr, KM_USER0); |
527 | if (cs->write) { | 538 | if (cs->write) { |
528 | flush_dcache_page(cs->pg); | 539 | flush_dcache_page(cs->pg); |
@@ -544,23 +555,39 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) | |||
544 | 555 | ||
545 | unlock_request(cs->fc, cs->req); | 556 | unlock_request(cs->fc, cs->req); |
546 | fuse_copy_finish(cs); | 557 | fuse_copy_finish(cs); |
547 | if (!cs->seglen) { | 558 | if (cs->pipebufs) { |
559 | struct pipe_buffer *buf = cs->pipebufs; | ||
560 | |||
561 | err = buf->ops->confirm(cs->pipe, buf); | ||
562 | if (err) | ||
563 | return err; | ||
564 | |||
548 | BUG_ON(!cs->nr_segs); | 565 | BUG_ON(!cs->nr_segs); |
549 | cs->seglen = cs->iov[0].iov_len; | 566 | cs->currbuf = buf; |
550 | cs->addr = (unsigned long) cs->iov[0].iov_base; | 567 | cs->mapaddr = buf->ops->map(cs->pipe, buf, 1); |
551 | cs->iov++; | 568 | cs->len = buf->len; |
569 | cs->buf = cs->mapaddr + buf->offset; | ||
570 | cs->pipebufs++; | ||
552 | cs->nr_segs--; | 571 | cs->nr_segs--; |
572 | } else { | ||
573 | if (!cs->seglen) { | ||
574 | BUG_ON(!cs->nr_segs); | ||
575 | cs->seglen = cs->iov[0].iov_len; | ||
576 | cs->addr = (unsigned long) cs->iov[0].iov_base; | ||
577 | cs->iov++; | ||
578 | cs->nr_segs--; | ||
579 | } | ||
580 | err = get_user_pages_fast(cs->addr, 1, cs->write, &cs->pg); | ||
581 | if (err < 0) | ||
582 | return err; | ||
583 | BUG_ON(err != 1); | ||
584 | offset = cs->addr % PAGE_SIZE; | ||
585 | cs->mapaddr = kmap_atomic(cs->pg, KM_USER0); | ||
586 | cs->buf = cs->mapaddr + offset; | ||
587 | cs->len = min(PAGE_SIZE - offset, cs->seglen); | ||
588 | cs->seglen -= cs->len; | ||
589 | cs->addr += cs->len; | ||
553 | } | 590 | } |
554 | err = get_user_pages_fast(cs->addr, 1, cs->write, &cs->pg); | ||
555 | if (err < 0) | ||
556 | return err; | ||
557 | BUG_ON(err != 1); | ||
558 | offset = cs->addr % PAGE_SIZE; | ||
559 | cs->mapaddr = kmap_atomic(cs->pg, KM_USER0); | ||
560 | cs->buf = cs->mapaddr + offset; | ||
561 | cs->len = min(PAGE_SIZE - offset, cs->seglen); | ||
562 | cs->seglen -= cs->len; | ||
563 | cs->addr += cs->len; | ||
564 | 591 | ||
565 | return lock_request(cs->fc, cs->req); | 592 | return lock_request(cs->fc, cs->req); |
566 | } | 593 | } |
@@ -984,23 +1011,17 @@ static int copy_out_args(struct fuse_copy_state *cs, struct fuse_out *out, | |||
984 | * it from the list and copy the rest of the buffer to the request. | 1011 | * it from the list and copy the rest of the buffer to the request. |
985 | * The request is finished by calling request_end() | 1012 | * The request is finished by calling request_end() |
986 | */ | 1013 | */ |
987 | static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov, | 1014 | static ssize_t fuse_dev_do_write(struct fuse_conn *fc, |
988 | unsigned long nr_segs, loff_t pos) | 1015 | struct fuse_copy_state *cs, size_t nbytes) |
989 | { | 1016 | { |
990 | int err; | 1017 | int err; |
991 | size_t nbytes = iov_length(iov, nr_segs); | ||
992 | struct fuse_req *req; | 1018 | struct fuse_req *req; |
993 | struct fuse_out_header oh; | 1019 | struct fuse_out_header oh; |
994 | struct fuse_copy_state cs; | ||
995 | struct fuse_conn *fc = fuse_get_conn(iocb->ki_filp); | ||
996 | if (!fc) | ||
997 | return -EPERM; | ||
998 | 1020 | ||
999 | fuse_copy_init(&cs, fc, 0, NULL, iov, nr_segs); | ||
1000 | if (nbytes < sizeof(struct fuse_out_header)) | 1021 | if (nbytes < sizeof(struct fuse_out_header)) |
1001 | return -EINVAL; | 1022 | return -EINVAL; |
1002 | 1023 | ||
1003 | err = fuse_copy_one(&cs, &oh, sizeof(oh)); | 1024 | err = fuse_copy_one(cs, &oh, sizeof(oh)); |
1004 | if (err) | 1025 | if (err) |
1005 | goto err_finish; | 1026 | goto err_finish; |
1006 | 1027 | ||
@@ -1013,7 +1034,7 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov, | |||
1013 | * and error contains notification code. | 1034 | * and error contains notification code. |
1014 | */ | 1035 | */ |
1015 | if (!oh.unique) { | 1036 | if (!oh.unique) { |
1016 | err = fuse_notify(fc, oh.error, nbytes - sizeof(oh), &cs); | 1037 | err = fuse_notify(fc, oh.error, nbytes - sizeof(oh), cs); |
1017 | return err ? err : nbytes; | 1038 | return err ? err : nbytes; |
1018 | } | 1039 | } |
1019 | 1040 | ||
@@ -1032,7 +1053,7 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov, | |||
1032 | 1053 | ||
1033 | if (req->aborted) { | 1054 | if (req->aborted) { |
1034 | spin_unlock(&fc->lock); | 1055 | spin_unlock(&fc->lock); |
1035 | fuse_copy_finish(&cs); | 1056 | fuse_copy_finish(cs); |
1036 | spin_lock(&fc->lock); | 1057 | spin_lock(&fc->lock); |
1037 | request_end(fc, req); | 1058 | request_end(fc, req); |
1038 | return -ENOENT; | 1059 | return -ENOENT; |
@@ -1049,7 +1070,7 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov, | |||
1049 | queue_interrupt(fc, req); | 1070 | queue_interrupt(fc, req); |
1050 | 1071 | ||
1051 | spin_unlock(&fc->lock); | 1072 | spin_unlock(&fc->lock); |
1052 | fuse_copy_finish(&cs); | 1073 | fuse_copy_finish(cs); |
1053 | return nbytes; | 1074 | return nbytes; |
1054 | } | 1075 | } |
1055 | 1076 | ||
@@ -1057,11 +1078,11 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov, | |||
1057 | list_move(&req->list, &fc->io); | 1078 | list_move(&req->list, &fc->io); |
1058 | req->out.h = oh; | 1079 | req->out.h = oh; |
1059 | req->locked = 1; | 1080 | req->locked = 1; |
1060 | cs.req = req; | 1081 | cs->req = req; |
1061 | spin_unlock(&fc->lock); | 1082 | spin_unlock(&fc->lock); |
1062 | 1083 | ||
1063 | err = copy_out_args(&cs, &req->out, nbytes); | 1084 | err = copy_out_args(cs, &req->out, nbytes); |
1064 | fuse_copy_finish(&cs); | 1085 | fuse_copy_finish(cs); |
1065 | 1086 | ||
1066 | spin_lock(&fc->lock); | 1087 | spin_lock(&fc->lock); |
1067 | req->locked = 0; | 1088 | req->locked = 0; |
@@ -1077,10 +1098,101 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov, | |||
1077 | err_unlock: | 1098 | err_unlock: |
1078 | spin_unlock(&fc->lock); | 1099 | spin_unlock(&fc->lock); |
1079 | err_finish: | 1100 | err_finish: |
1080 | fuse_copy_finish(&cs); | 1101 | fuse_copy_finish(cs); |
1081 | return err; | 1102 | return err; |
1082 | } | 1103 | } |
1083 | 1104 | ||
1105 | static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov, | ||
1106 | unsigned long nr_segs, loff_t pos) | ||
1107 | { | ||
1108 | struct fuse_copy_state cs; | ||
1109 | struct fuse_conn *fc = fuse_get_conn(iocb->ki_filp); | ||
1110 | if (!fc) | ||
1111 | return -EPERM; | ||
1112 | |||
1113 | fuse_copy_init(&cs, fc, 0, NULL, iov, nr_segs); | ||
1114 | |||
1115 | return fuse_dev_do_write(fc, &cs, iov_length(iov, nr_segs)); | ||
1116 | } | ||
1117 | |||
1118 | static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, | ||
1119 | struct file *out, loff_t *ppos, | ||
1120 | size_t len, unsigned int flags) | ||
1121 | { | ||
1122 | unsigned nbuf; | ||
1123 | unsigned idx; | ||
1124 | struct pipe_buffer *bufs; | ||
1125 | struct fuse_copy_state cs; | ||
1126 | struct fuse_conn *fc; | ||
1127 | size_t rem; | ||
1128 | ssize_t ret; | ||
1129 | |||
1130 | fc = fuse_get_conn(out); | ||
1131 | if (!fc) | ||
1132 | return -EPERM; | ||
1133 | |||
1134 | bufs = kmalloc(pipe->buffers * sizeof (struct pipe_buffer), GFP_KERNEL); | ||
1135 | if (!bufs) | ||
1136 | return -ENOMEM; | ||
1137 | |||
1138 | pipe_lock(pipe); | ||
1139 | nbuf = 0; | ||
1140 | rem = 0; | ||
1141 | for (idx = 0; idx < pipe->nrbufs && rem < len; idx++) | ||
1142 | rem += pipe->bufs[(pipe->curbuf + idx) & (pipe->buffers - 1)].len; | ||
1143 | |||
1144 | ret = -EINVAL; | ||
1145 | if (rem < len) { | ||
1146 | pipe_unlock(pipe); | ||
1147 | goto out; | ||
1148 | } | ||
1149 | |||
1150 | rem = len; | ||
1151 | while (rem) { | ||
1152 | struct pipe_buffer *ibuf; | ||
1153 | struct pipe_buffer *obuf; | ||
1154 | |||
1155 | BUG_ON(nbuf >= pipe->buffers); | ||
1156 | BUG_ON(!pipe->nrbufs); | ||
1157 | ibuf = &pipe->bufs[pipe->curbuf]; | ||
1158 | obuf = &bufs[nbuf]; | ||
1159 | |||
1160 | if (rem >= ibuf->len) { | ||
1161 | *obuf = *ibuf; | ||
1162 | ibuf->ops = NULL; | ||
1163 | pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1); | ||
1164 | pipe->nrbufs--; | ||
1165 | } else { | ||
1166 | ibuf->ops->get(pipe, ibuf); | ||
1167 | *obuf = *ibuf; | ||
1168 | obuf->flags &= ~PIPE_BUF_FLAG_GIFT; | ||
1169 | obuf->len = rem; | ||
1170 | ibuf->offset += obuf->len; | ||
1171 | ibuf->len -= obuf->len; | ||
1172 | } | ||
1173 | nbuf++; | ||
1174 | rem -= obuf->len; | ||
1175 | } | ||
1176 | pipe_unlock(pipe); | ||
1177 | |||
1178 | memset(&cs, 0, sizeof(struct fuse_copy_state)); | ||
1179 | cs.fc = fc; | ||
1180 | cs.write = 0; | ||
1181 | cs.pipebufs = bufs; | ||
1182 | cs.nr_segs = nbuf; | ||
1183 | cs.pipe = pipe; | ||
1184 | |||
1185 | ret = fuse_dev_do_write(fc, &cs, len); | ||
1186 | |||
1187 | for (idx = 0; idx < nbuf; idx++) { | ||
1188 | struct pipe_buffer *buf = &bufs[idx]; | ||
1189 | buf->ops->release(pipe, buf); | ||
1190 | } | ||
1191 | out: | ||
1192 | kfree(bufs); | ||
1193 | return ret; | ||
1194 | } | ||
1195 | |||
1084 | static unsigned fuse_dev_poll(struct file *file, poll_table *wait) | 1196 | static unsigned fuse_dev_poll(struct file *file, poll_table *wait) |
1085 | { | 1197 | { |
1086 | unsigned mask = POLLOUT | POLLWRNORM; | 1198 | unsigned mask = POLLOUT | POLLWRNORM; |
@@ -1224,6 +1336,7 @@ const struct file_operations fuse_dev_operations = { | |||
1224 | .aio_read = fuse_dev_read, | 1336 | .aio_read = fuse_dev_read, |
1225 | .write = do_sync_write, | 1337 | .write = do_sync_write, |
1226 | .aio_write = fuse_dev_write, | 1338 | .aio_write = fuse_dev_write, |
1339 | .splice_write = fuse_dev_splice_write, | ||
1227 | .poll = fuse_dev_poll, | 1340 | .poll = fuse_dev_poll, |
1228 | .release = fuse_dev_release, | 1341 | .release = fuse_dev_release, |
1229 | .fasync = fuse_dev_fasync, | 1342 | .fasync = fuse_dev_fasync, |