⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dev.c

📁 linux下的用户文件系统fuse-2.5.2
💻 C
📖 第 1 页 / 共 2 页
字号:
			   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(&current->mm->mmap_sem);	err = get_user_pages(current, current->mm, cs->addr, 1, cs->write, 0,			     &cs->pg, NULL);	up_read(&current->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 + -