📄 dev.c
字号:
unsigned long nr_segs){ memset(cs, 0, sizeof(*cs)); cs->write = write; cs->req = req; cs->iov = iov; cs->nr_segs = nr_segs;}/* Unmap and put previous page of userspace buffer */static inline void fuse_copy_finish(struct fuse_copy_state *cs){ if (cs->mapaddr) { kunmap_atomic(cs->mapaddr, KM_USER0); if (cs->write) { flush_dcache_page(cs->pg); set_page_dirty_lock(cs->pg); } put_page(cs->pg); cs->mapaddr = NULL; }}/* * Get another pagefull of userspace buffer, and map it to kernel * address space, and lock request */static int fuse_copy_fill(struct fuse_copy_state *cs){ unsigned long offset; int err; unlock_request(cs->req); fuse_copy_finish(cs); if (!cs->seglen) { BUG_ON(!cs->nr_segs); cs->seglen = cs->iov[0].iov_len; cs->addr = (unsigned long) cs->iov[0].iov_base; cs->iov ++; cs->nr_segs --; } down_read(¤t->mm->mmap_sem); err = get_user_pages(current, current->mm, cs->addr, 1, cs->write, 0, &cs->pg, NULL); up_read(¤t->mm->mmap_sem); if (err < 0) return err; BUG_ON(err != 1); offset = cs->addr % PAGE_SIZE; cs->mapaddr = kmap_atomic(cs->pg, KM_USER0); cs->buf = cs->mapaddr + offset; cs->len = min(PAGE_SIZE - offset, cs->seglen); cs->seglen -= cs->len; cs->addr += cs->len; return lock_request(cs->req);}/* Do as much copy to/from userspace buffer as we can */static inline int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size){ unsigned ncpy = min(*size, cs->len); if (val) { if (cs->write) memcpy(cs->buf, *val, ncpy); else memcpy(*val, cs->buf, ncpy); *val += ncpy; } *size -= ncpy; cs->len -= ncpy; cs->buf += ncpy; return ncpy;}/* * Copy a page in the request to/from the userspace buffer. Must be * done atomically */static inline int fuse_copy_page(struct fuse_copy_state *cs, struct page *page, unsigned offset, unsigned count, int zeroing){ if (page && zeroing && count < PAGE_SIZE) { void *mapaddr = kmap_atomic(page, KM_USER1); memset(mapaddr, 0, PAGE_SIZE); kunmap_atomic(mapaddr, KM_USER1); } while (count) { int err; if (!cs->len && (err = fuse_copy_fill(cs))) return err; if (page) { void *mapaddr = kmap_atomic(page, KM_USER1); void *buf = mapaddr + offset; offset += fuse_copy_do(cs, &buf, &count); kunmap_atomic(mapaddr, KM_USER1); } else offset += fuse_copy_do(cs, NULL, &count); } if (page && !cs->write) flush_dcache_page(page); return 0;}/* Copy pages in the request to/from userspace buffer */static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes, int zeroing){ unsigned i; struct fuse_req *req = cs->req; unsigned offset = req->page_offset; unsigned count = min(nbytes, (unsigned) PAGE_SIZE - offset); for (i = 0; i < req->num_pages && (nbytes || zeroing); i++) { struct page *page = req->pages[i]; int err = fuse_copy_page(cs, page, offset, count, zeroing); if (err) return err; nbytes -= count; count = min(nbytes, (unsigned) PAGE_SIZE); offset = 0; } return 0;}/* Copy a single argument in the request to/from userspace buffer */static int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size){ while (size) { int err; if (!cs->len && (err = fuse_copy_fill(cs))) return err; fuse_copy_do(cs, &val, &size); } return 0;}/* Copy request arguments to/from userspace buffer */static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs, unsigned argpages, struct fuse_arg *args, int zeroing){ int err = 0; unsigned i; for (i = 0; !err && i < numargs; i++) { struct fuse_arg *arg = &args[i]; if (i == numargs - 1 && argpages) err = fuse_copy_pages(cs, arg->size, zeroing); else err = fuse_copy_one(cs, arg->value, arg->size); } return err;}/* Wait until a request is available on the pending list */static void request_wait(struct fuse_conn *fc){ DECLARE_WAITQUEUE(wait, current); add_wait_queue_exclusive(&fc->waitq, &wait); while (fc->mounted && list_empty(&fc->pending)) { set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) break; spin_unlock(&fuse_lock); schedule(); spin_lock(&fuse_lock); } set_current_state(TASK_RUNNING); remove_wait_queue(&fc->waitq, &wait);}#ifndef KERNEL_2_6static inline size_t iov_length(const struct iovec *iov, unsigned long nr_segs){ unsigned long seg; size_t ret = 0; for (seg = 0; seg < nr_segs; seg++) ret += iov[seg].iov_len; return ret;}#endif/* * Read a single request into the userspace filesystem's buffer. This * function waits until a request is available, then removes it from * the pending list and copies request data to userspace buffer. If * no reply is needed (FORGET) or request has been interrupted or * there was an error during the copying then it's finished by calling * request_end(). Otherwise add it to the processing list, and set * the 'sent' flag. */static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, unsigned long nr_segs, loff_t *off){ int err; struct fuse_conn *fc; struct fuse_req *req; struct fuse_in *in; struct fuse_copy_state cs; unsigned reqsize; restart: spin_lock(&fuse_lock); fc = file->private_data; err = -EPERM; if (!fc) goto err_unlock; request_wait(fc); err = -ENODEV; if (!fc->mounted) goto err_unlock; err = -ERESTARTSYS; if (list_empty(&fc->pending)) goto err_unlock; req = list_entry(fc->pending.next, struct fuse_req, list); list_del_init(&req->list); in = &req->in; reqsize = in->h.len; /* If request is too large, reply with an error and restart the read */ if (iov_length(iov, nr_segs) < reqsize) { req->out.h.error = -EIO; /* SETXATTR is special, since it may contain too large data */ if (in->h.opcode == FUSE_SETXATTR) req->out.h.error = -E2BIG; request_end(fc, req); goto restart; } spin_unlock(&fuse_lock); fuse_copy_init(&cs, 1, req, iov, nr_segs); err = fuse_copy_one(&cs, &in->h, sizeof(in->h)); if (!err) err = fuse_copy_args(&cs, in->numargs, in->argpages, (struct fuse_arg *) in->args, 0); fuse_copy_finish(&cs); spin_lock(&fuse_lock); req->locked = 0; if (!err && req->interrupted) err = -ENOENT; if (err) { if (!req->interrupted) req->out.h.error = -EIO; request_end(fc, req); return err; } if (!req->isreply) request_end(fc, req); else { req->sent = 1; list_add_tail(&req->list, &fc->processing); spin_unlock(&fuse_lock); } return reqsize; err_unlock: spin_unlock(&fuse_lock); return err;}static ssize_t fuse_dev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *off){ struct iovec iov; iov.iov_len = nbytes; iov.iov_base = buf; return fuse_dev_readv(file, &iov, 1, off);}/* Look up request on processing list by unique ID */static struct fuse_req *request_find(struct fuse_conn *fc, u64 unique){ struct list_head *entry; list_for_each(entry, &fc->processing) { struct fuse_req *req; req = list_entry(entry, struct fuse_req, list); if (req->in.h.unique == unique) return req; } return NULL;}static int copy_out_args(struct fuse_copy_state *cs, struct fuse_out *out, unsigned nbytes){ unsigned reqsize = sizeof(struct fuse_out_header); if (out->h.error) return nbytes != reqsize ? -EINVAL : 0; reqsize += len_args(out->numargs, out->args); if (reqsize < nbytes || (reqsize > nbytes && !out->argvar)) return -EINVAL; else if (reqsize > nbytes) { struct fuse_arg *lastarg = &out->args[out->numargs-1]; unsigned diffsize = reqsize - nbytes; if (diffsize > lastarg->size) return -EINVAL; lastarg->size -= diffsize; } return fuse_copy_args(cs, out->numargs, out->argpages, out->args, out->page_zeroing);}/* * Write a single reply to a request. First the header is copied from * the write buffer. The request is then searched on the processing * list by the unique ID found in the header. If found, then remove * it from the list and copy the rest of the buffer to the request. * The request is finished by calling request_end() */static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov, unsigned long nr_segs, loff_t *off){ int err; unsigned nbytes = iov_length(iov, nr_segs); struct fuse_req *req; struct fuse_out_header oh; struct fuse_copy_state cs; struct fuse_conn *fc = fuse_get_conn(file); if (!fc) return -ENODEV; fuse_copy_init(&cs, 0, NULL, iov, nr_segs); if (nbytes < sizeof(struct fuse_out_header)) return -EINVAL; err = fuse_copy_one(&cs, &oh, sizeof(oh)); if (err) goto err_finish; err = -EINVAL; if (!oh.unique || oh.error <= -1000 || oh.error > 0 || oh.len != nbytes) goto err_finish; spin_lock(&fuse_lock); req = request_find(fc, oh.unique); err = -EINVAL; if (!req) goto err_unlock; list_del_init(&req->list); if (req->interrupted) { spin_unlock(&fuse_lock); fuse_copy_finish(&cs); spin_lock(&fuse_lock); request_end(fc, req); return -ENOENT; } req->out.h = oh; req->locked = 1; cs.req = req; spin_unlock(&fuse_lock); err = copy_out_args(&cs, &req->out, nbytes); fuse_copy_finish(&cs); spin_lock(&fuse_lock); req->locked = 0; if (!err) { if (req->interrupted) err = -ENOENT; } else if (!req->interrupted) req->out.h.error = -EIO; request_end(fc, req); return err ? err : nbytes; err_unlock: spin_unlock(&fuse_lock); err_finish: fuse_copy_finish(&cs); return err;}static ssize_t fuse_dev_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *off){ struct iovec iov; iov.iov_len = nbytes; iov.iov_base = (char __user *) buf; return fuse_dev_writev(file, &iov, 1, off);}static unsigned fuse_dev_poll(struct file *file, poll_table *wait){ struct fuse_conn *fc = fuse_get_conn(file); unsigned mask = POLLOUT | POLLWRNORM; if (!fc) return -ENODEV; poll_wait(file, &fc->waitq, wait); spin_lock(&fuse_lock); if (!list_empty(&fc->pending)) mask |= POLLIN | POLLRDNORM; spin_unlock(&fuse_lock); return mask;}/* Abort all requests on the given list (pending or processing) */static void end_requests(struct fuse_conn *fc, struct list_head *head){ while (!list_empty(head)) { struct fuse_req *req; req = list_entry(head->next, struct fuse_req, list); list_del_init(&req->list); req->out.h.error = -ECONNABORTED; request_end(fc, req); spin_lock(&fuse_lock); }}static int fuse_dev_release(struct inode *inode, struct file *file){ struct fuse_conn *fc; spin_lock(&fuse_lock); fc = file->private_data; if (fc) { fc->connected = 0; end_requests(fc, &fc->pending); end_requests(fc, &fc->processing); fuse_release_conn(fc); } spin_unlock(&fuse_lock); return 0;}struct file_operations fuse_dev_operations = { .owner = THIS_MODULE, .llseek = no_llseek, .read = fuse_dev_read, .readv = fuse_dev_readv, .write = fuse_dev_write, .writev = fuse_dev_writev, .poll = fuse_dev_poll, .release = fuse_dev_release,};static struct miscdevice fuse_miscdevice = { .minor = FUSE_MINOR, .name = "fuse", .fops = &fuse_dev_operations,};int __init fuse_dev_init(void){ int err = -ENOMEM; fuse_req_cachep = kmem_cache_create("fuse_request", sizeof(struct fuse_req), 0, 0, NULL, NULL); if (!fuse_req_cachep) goto out; err = misc_register(&fuse_miscdevice); if (err) goto out_cache_clean; return 0; out_cache_clean: kmem_cache_destroy(fuse_req_cachep); out: return err;}void fuse_dev_cleanup(void){ misc_deregister(&fuse_miscdevice); kmem_cache_destroy(fuse_req_cachep);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -