[PATCH] v9fs: transport modules
This part of the patch contains transport routines. Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com> Signed-off-by: Latchesar Ionkov <lucho@ionkov.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
		
							parent
							
								
									b8cf945b31
								
							
						
					
					
						commit
						426cc91aa6
					
				
							
								
								
									
										440
									
								
								fs/9p/mux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										440
									
								
								fs/9p/mux.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,440 @@ | ||||
| /*
 | ||||
|  * linux/fs/9p/mux.c | ||||
|  * | ||||
|  * Protocol Multiplexer | ||||
|  * | ||||
|  *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||||
|  *  Copyright (C) 2004 by Latchesar Ionkov <lucho@ionkov.net> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to: | ||||
|  *  Free Software Foundation | ||||
|  *  51 Franklin Street, Fifth Floor | ||||
|  *  Boston, MA  02111-1301  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/config.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/fs.h> | ||||
| #include <linux/kthread.h> | ||||
| #include <linux/idr.h> | ||||
| 
 | ||||
| #include "debug.h" | ||||
| #include "v9fs.h" | ||||
| #include "9p.h" | ||||
| #include "transport.h" | ||||
| #include "conv.h" | ||||
| #include "mux.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * dprintcond - print condition of session info | ||||
|  * @v9ses: session info structure | ||||
|  * @req: RPC request structure | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static inline int | ||||
| dprintcond(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req) | ||||
| { | ||||
| 	dprintk(DEBUG_MUX, "condition: %d, %p\n", v9ses->transport->status, | ||||
| 		req->rcall); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * xread - force read of a certain number of bytes | ||||
|  * @v9ses: session info structure | ||||
|  * @ptr: pointer to buffer | ||||
|  * @sz: number of bytes to read | ||||
|  * | ||||
|  * Chuck Cranor CS-533 project1 | ||||
|  */ | ||||
| 
 | ||||
| static int xread(struct v9fs_session_info *v9ses, void *ptr, unsigned long sz) | ||||
| { | ||||
| 	int rd = 0; | ||||
| 	int ret = 0; | ||||
| 	while (rd < sz) { | ||||
| 		ret = v9ses->transport->read(v9ses->transport, ptr, sz - rd); | ||||
| 		if (ret <= 0) { | ||||
| 			dprintk(DEBUG_ERROR, "xread errno %d\n", ret); | ||||
| 			return ret; | ||||
| 		} | ||||
| 		rd += ret; | ||||
| 		ptr += ret; | ||||
| 	} | ||||
| 	return (rd); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * read_message - read a full 9P2000 fcall packet | ||||
|  * @v9ses: session info structure | ||||
|  * @rcall: fcall structure to read into | ||||
|  * @rcalllen: size of fcall buffer | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static int | ||||
| read_message(struct v9fs_session_info *v9ses, | ||||
| 	     struct v9fs_fcall *rcall, int rcalllen) | ||||
| { | ||||
| 	unsigned char buf[4]; | ||||
| 	void *data; | ||||
| 	int size = 0; | ||||
| 	int res = 0; | ||||
| 
 | ||||
| 	res = xread(v9ses, buf, sizeof(buf)); | ||||
| 	if (res < 0) { | ||||
| 		dprintk(DEBUG_ERROR, | ||||
| 			"Reading of count field failed returned: %d\n", res); | ||||
| 		return res; | ||||
| 	} | ||||
| 
 | ||||
| 	if (res < 4) { | ||||
| 		dprintk(DEBUG_ERROR, | ||||
| 			"Reading of count field failed returned: %d\n", res); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	size = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); | ||||
| 	dprintk(DEBUG_MUX, "got a packet count: %d\n", size); | ||||
| 
 | ||||
| 	/* adjust for the four bytes of size */ | ||||
| 	size -= 4; | ||||
| 
 | ||||
| 	if (size > v9ses->maxdata) { | ||||
| 		dprintk(DEBUG_ERROR, "packet too big: %d\n", size); | ||||
| 		return -E2BIG; | ||||
| 	} | ||||
| 
 | ||||
| 	data = kmalloc(size, GFP_KERNEL); | ||||
| 	if (!data) { | ||||
| 		eprintk(KERN_WARNING, "out of memory\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	res = xread(v9ses, data, size); | ||||
| 	if (res < size) { | ||||
| 		dprintk(DEBUG_ERROR, "Reading of fcall failed returned: %d\n", | ||||
| 			res); | ||||
| 		kfree(data); | ||||
| 		return res; | ||||
| 	} | ||||
| 
 | ||||
| 	/* we now have an in-memory string that is the reply.
 | ||||
| 	 * deserialize it. There is very little to go wrong at this point | ||||
| 	 * save for v9fs_alloc errors. | ||||
| 	 */ | ||||
| 	res = v9fs_deserialize_fcall(v9ses, size, data, v9ses->maxdata, | ||||
| 				     rcall, rcalllen); | ||||
| 
 | ||||
| 	kfree(data); | ||||
| 
 | ||||
| 	if (res < 0) | ||||
| 		return res; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_recv - receive an RPC response for a particular tag | ||||
|  * @v9ses: session info structure | ||||
|  * @req: RPC request structure | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static int v9fs_recv(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	dprintk(DEBUG_MUX, "waiting for response: %d\n", req->tcall->tag); | ||||
| 	ret = wait_event_interruptible(v9ses->read_wait, | ||||
| 		       ((v9ses->transport->status != Connected) || | ||||
| 			(req->rcall != 0) || dprintcond(v9ses, req))); | ||||
| 
 | ||||
| 	dprintk(DEBUG_MUX, "got it: rcall %p\n", req->rcall); | ||||
| 	if (v9ses->transport->status == Disconnected) | ||||
| 		return -ECONNRESET; | ||||
| 
 | ||||
| 	if (ret == 0) { | ||||
| 		spin_lock(&v9ses->muxlock); | ||||
| 		list_del(&req->next); | ||||
| 		spin_unlock(&v9ses->muxlock); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_send - send a 9P request | ||||
|  * @v9ses: session info structure | ||||
|  * @req: RPC request to send | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static int v9fs_send(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req) | ||||
| { | ||||
| 	int ret = -1; | ||||
| 	void *data = NULL; | ||||
| 	struct v9fs_fcall *tcall = req->tcall; | ||||
| 
 | ||||
| 	data = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL); | ||||
| 	if (!data) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	tcall->size = 0;	/* enforce size recalculation */ | ||||
| 	ret = | ||||
| 	    v9fs_serialize_fcall(v9ses, tcall, data, | ||||
| 				 v9ses->maxdata + V9FS_IOHDRSZ); | ||||
| 	if (ret < 0) | ||||
| 		goto free_data; | ||||
| 
 | ||||
| 	spin_lock(&v9ses->muxlock); | ||||
| 	list_add(&req->next, &v9ses->mux_fcalls); | ||||
| 	spin_unlock(&v9ses->muxlock); | ||||
| 
 | ||||
| 	dprintk(DEBUG_MUX, "sending message: tag %d size %d\n", tcall->tag, | ||||
| 		tcall->size); | ||||
| 	ret = v9ses->transport->write(v9ses->transport, data, tcall->size); | ||||
| 
 | ||||
| 	if (ret != tcall->size) { | ||||
| 		spin_lock(&v9ses->muxlock); | ||||
| 		list_del(&req->next); | ||||
| 		kfree(req->rcall); | ||||
| 
 | ||||
| 		spin_unlock(&v9ses->muxlock); | ||||
| 		if (ret >= 0) | ||||
| 			ret = -EREMOTEIO; | ||||
| 	} else | ||||
| 		ret = 0; | ||||
| 
 | ||||
|       free_data: | ||||
| 	kfree(data); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_mux_rpc - send a request, receive a response | ||||
|  * @v9ses: session info structure | ||||
|  * @tcall: fcall to send | ||||
|  * @rcall: buffer to place response into | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| long | ||||
| v9fs_mux_rpc(struct v9fs_session_info *v9ses, struct v9fs_fcall *tcall, | ||||
| 	     struct v9fs_fcall **rcall) | ||||
| { | ||||
| 	int tid = -1; | ||||
| 	struct v9fs_fcall *fcall = NULL; | ||||
| 	struct v9fs_rpcreq req; | ||||
| 	int ret = -1; | ||||
| 
 | ||||
| 	if (!v9ses) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (rcall) | ||||
| 		*rcall = NULL; | ||||
| 
 | ||||
| 	if (tcall->id != TVERSION) { | ||||
| 		tid = v9fs_get_idpool(&v9ses->tidpool); | ||||
| 		if (tid < 0) | ||||
| 			return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	tcall->tag = tid; | ||||
| 
 | ||||
| 	req.tcall = tcall; | ||||
| 	req.rcall = NULL; | ||||
| 
 | ||||
| 	ret = v9fs_send(v9ses, &req); | ||||
| 
 | ||||
| 	if (ret < 0) { | ||||
| 		if (tcall->id != TVERSION) | ||||
| 			v9fs_put_idpool(tid, &v9ses->tidpool); | ||||
| 		dprintk(DEBUG_MUX, "error %d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = v9fs_recv(v9ses, &req); | ||||
| 
 | ||||
| 	fcall = req.rcall; | ||||
| 
 | ||||
| 	dprintk(DEBUG_MUX, "received: tag=%x, ret=%d\n", tcall->tag, ret); | ||||
| 	if (ret == -ERESTARTSYS) { | ||||
| 		if (v9ses->transport->status != Disconnected | ||||
| 		    && tcall->id != TFLUSH) { | ||||
| 			unsigned long flags; | ||||
| 
 | ||||
| 			dprintk(DEBUG_MUX, "flushing the tag: %d\n", | ||||
| 				tcall->tag); | ||||
| 			clear_thread_flag(TIF_SIGPENDING); | ||||
| 			v9fs_t_flush(v9ses, tcall->tag); | ||||
| 			spin_lock_irqsave(¤t->sighand->siglock, flags); | ||||
| 			recalc_sigpending(); | ||||
| 			spin_unlock_irqrestore(¤t->sighand->siglock, | ||||
| 					       flags); | ||||
| 			dprintk(DEBUG_MUX, "flushing done\n"); | ||||
| 		} | ||||
| 
 | ||||
| 		goto release_req; | ||||
| 	} else if (ret < 0) | ||||
| 		goto release_req; | ||||
| 
 | ||||
| 	if (!fcall) | ||||
| 		ret = -EIO; | ||||
| 	else { | ||||
| 		if (fcall->id == RERROR) { | ||||
| 			ret = v9fs_errstr2errno(fcall->params.rerror.error); | ||||
| 			if (ret == 0) {	/* string match failed */ | ||||
| 				if (fcall->params.rerror.errno) | ||||
| 					ret = -(fcall->params.rerror.errno); | ||||
| 				else | ||||
| 					ret = -ESERVERFAULT; | ||||
| 			} | ||||
| 		} else if (fcall->id != tcall->id + 1) { | ||||
| 			dprintk(DEBUG_ERROR, | ||||
| 				"fcall mismatch: expected %d, got %d\n", | ||||
| 				tcall->id + 1, fcall->id); | ||||
| 			ret = -EIO; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|       release_req: | ||||
| 	if (tcall->id != TVERSION) | ||||
| 		v9fs_put_idpool(tid, &v9ses->tidpool); | ||||
| 	if (rcall) | ||||
| 		*rcall = fcall; | ||||
| 	else | ||||
| 		kfree(fcall); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_recvproc - kproc to handle demultiplexing responses | ||||
|  * @data: session info structure | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static int v9fs_recvproc(void *data) | ||||
| { | ||||
| 	struct v9fs_session_info *v9ses = (struct v9fs_session_info *)data; | ||||
| 	struct v9fs_fcall *rcall = NULL; | ||||
| 	struct v9fs_rpcreq *rptr; | ||||
| 	struct v9fs_rpcreq *req; | ||||
| 	struct v9fs_rpcreq *rreq; | ||||
| 	int err = 0; | ||||
| 
 | ||||
| 	allow_signal(SIGKILL); | ||||
| 	set_current_state(TASK_INTERRUPTIBLE); | ||||
| 	complete(&v9ses->proccmpl); | ||||
| 	while (!kthread_should_stop() && err >= 0) { | ||||
| 		req = rptr = rreq = NULL; | ||||
| 
 | ||||
| 		rcall = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL); | ||||
| 		if (!rcall) { | ||||
| 			eprintk(KERN_ERR, "no memory for buffers\n"); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		err = read_message(v9ses, rcall, v9ses->maxdata + V9FS_IOHDRSZ); | ||||
| 		if (err < 0) { | ||||
| 			kfree(rcall); | ||||
| 			break; | ||||
| 		} | ||||
| 		spin_lock(&v9ses->muxlock); | ||||
| 		list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) { | ||||
| 			if (rreq->tcall->tag == rcall->tag) { | ||||
| 				req = rreq; | ||||
| 				req->rcall = rcall; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (req && (req->tcall->id == TFLUSH)) { | ||||
| 			struct v9fs_rpcreq *treq = NULL; | ||||
| 			list_for_each_entry_safe(treq, rptr, &v9ses->mux_fcalls, next) { | ||||
| 				if (treq->tcall->tag == | ||||
| 				    req->tcall->params.tflush.oldtag) { | ||||
| 					list_del(&rptr->next); | ||||
| 					kfree(treq->rcall); | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		spin_unlock(&v9ses->muxlock); | ||||
| 
 | ||||
| 		if (!req) { | ||||
| 			dprintk(DEBUG_ERROR, | ||||
| 				"unexpected response: id %d tag %d\n", | ||||
| 				rcall->id, rcall->tag); | ||||
| 
 | ||||
| 			kfree(rcall); | ||||
| 		} | ||||
| 
 | ||||
| 		wake_up_all(&v9ses->read_wait); | ||||
| 		set_current_state(TASK_INTERRUPTIBLE); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Inform all pending processes about the failure */ | ||||
| 	wake_up_all(&v9ses->read_wait); | ||||
| 
 | ||||
| 	if (signal_pending(current)) | ||||
| 		complete(&v9ses->proccmpl); | ||||
| 
 | ||||
| 	dprintk(DEBUG_MUX, "recvproc: end\n"); | ||||
| 	v9ses->recvproc = NULL; | ||||
| 
 | ||||
| 	return err >= 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_mux_init - initialize multiplexer (spawn kproc) | ||||
|  * @v9ses: session info structure | ||||
|  * @dev_name: mount device information (to create unique kproc) | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| int v9fs_mux_init(struct v9fs_session_info *v9ses, const char *dev_name) | ||||
| { | ||||
| 	char procname[60]; | ||||
| 
 | ||||
| 	strncpy(procname, dev_name, sizeof(procname)); | ||||
| 	procname[sizeof(procname) - 1] = 0; | ||||
| 
 | ||||
| 	init_waitqueue_head(&v9ses->read_wait); | ||||
| 	init_completion(&v9ses->fcread); | ||||
| 	init_completion(&v9ses->proccmpl); | ||||
| 	spin_lock_init(&v9ses->muxlock); | ||||
| 	INIT_LIST_HEAD(&v9ses->mux_fcalls); | ||||
| 	v9ses->recvproc = NULL; | ||||
| 	v9ses->curfcall = NULL; | ||||
| 
 | ||||
| 	v9ses->recvproc = kthread_create(v9fs_recvproc, v9ses, | ||||
| 					 "v9fs_recvproc %s", procname); | ||||
| 
 | ||||
| 	if (IS_ERR(v9ses->recvproc)) { | ||||
| 		eprintk(KERN_ERR, "cannot create receiving thread\n"); | ||||
| 		v9fs_session_close(v9ses); | ||||
| 		return -ECONNABORTED; | ||||
| 	} | ||||
| 
 | ||||
| 	wake_up_process(v9ses->recvproc); | ||||
| 	wait_for_completion(&v9ses->proccmpl); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										39
									
								
								fs/9p/mux.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								fs/9p/mux.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| /*
 | ||||
|  * linux/fs/9p/mux.h | ||||
|  * | ||||
|  * Multiplexer Definitions | ||||
|  * | ||||
|  *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to: | ||||
|  *  Free Software Foundation | ||||
|  *  51 Franklin Street, Fifth Floor | ||||
|  *  Boston, MA  02111-1301  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /* structure to manage each RPC transaction */ | ||||
| 
 | ||||
| struct v9fs_rpcreq { | ||||
| 	struct v9fs_fcall *tcall; | ||||
| 	struct v9fs_fcall *rcall; | ||||
| 
 | ||||
| 	/* XXX - could we put scatter/gather buffers here? */ | ||||
| 
 | ||||
| 	struct list_head next; | ||||
| }; | ||||
| 
 | ||||
| int v9fs_mux_init(struct v9fs_session_info *v9ses, const char *dev_name); | ||||
| long v9fs_mux_rpc(struct v9fs_session_info *v9ses, | ||||
| 		  struct v9fs_fcall *tcall, struct v9fs_fcall **rcall); | ||||
							
								
								
									
										172
									
								
								fs/9p/trans_fd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								fs/9p/trans_fd.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,172 @@ | ||||
| /*
 | ||||
|  * linux/fs/9p/trans_fd.c | ||||
|  * | ||||
|  * File Descriptor Transport Layer | ||||
|  * | ||||
|  *  Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to: | ||||
|  *  Free Software Foundation | ||||
|  *  51 Franklin Street, Fifth Floor | ||||
|  *  Boston, MA  02111-1301  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/config.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/net.h> | ||||
| #include <linux/ipv6.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/un.h> | ||||
| #include <asm/uaccess.h> | ||||
| #include <linux/inet.h> | ||||
| #include <linux/idr.h> | ||||
| #include <linux/file.h> | ||||
| 
 | ||||
| #include "debug.h" | ||||
| #include "v9fs.h" | ||||
| #include "transport.h" | ||||
| 
 | ||||
| struct v9fs_trans_fd { | ||||
| 	struct file *in_file; | ||||
| 	struct file *out_file; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_fd_recv - receive from a socket | ||||
|  * @v9ses: session information | ||||
|  * @v: buffer to receive data into | ||||
|  * @len: size of receive buffer | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static int v9fs_fd_recv(struct v9fs_transport *trans, void *v, int len) | ||||
| { | ||||
| 	struct v9fs_trans_fd *ts = trans ? trans->priv : NULL; | ||||
| 
 | ||||
| 	if (!trans || trans->status != Connected || !ts) | ||||
| 		return -EIO; | ||||
| 
 | ||||
| 	return kernel_read(ts->in_file, ts->in_file->f_pos, v, len); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_fd_send - send to a socket | ||||
|  * @v9ses: session information | ||||
|  * @v: buffer to send data from | ||||
|  * @len: size of send buffer | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static int v9fs_fd_send(struct v9fs_transport *trans, void *v, int len) | ||||
| { | ||||
| 	struct v9fs_trans_fd *ts = trans ? trans->priv : NULL; | ||||
| 	mm_segment_t oldfs = get_fs(); | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (!trans || trans->status != Connected || !ts) | ||||
| 		return -EIO; | ||||
| 
 | ||||
| 	set_fs(get_ds()); | ||||
| 	/* The cast to a user pointer is valid due to the set_fs() */ | ||||
| 	ret = vfs_write(ts->out_file, (void __user *)v, len, &ts->out_file->f_pos); | ||||
| 	set_fs(oldfs); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_fd_init - initialize file descriptor transport | ||||
|  * @v9ses: session information | ||||
|  * @addr: address of server to mount | ||||
|  * @data: mount options | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static int | ||||
| v9fs_fd_init(struct v9fs_session_info *v9ses, const char *addr, char *data) | ||||
| { | ||||
| 	struct v9fs_trans_fd *ts = NULL; | ||||
| 	struct v9fs_transport *trans = v9ses->transport; | ||||
| 
 | ||||
| 	if((v9ses->wfdno == ~0) || (v9ses->rfdno == ~0)) { | ||||
| 		printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n"); | ||||
| 		return -ENOPROTOOPT; | ||||
| 	} | ||||
| 
 | ||||
| 	sema_init(&trans->writelock, 1); | ||||
| 	sema_init(&trans->readlock, 1); | ||||
| 
 | ||||
| 	ts = kmalloc(sizeof(struct v9fs_trans_fd), GFP_KERNEL); | ||||
| 
 | ||||
| 	if (!ts) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	ts->in_file = fget( v9ses->rfdno ); | ||||
| 	ts->out_file = fget( v9ses->wfdno ); | ||||
| 
 | ||||
| 	if (!ts->in_file || !ts->out_file) { | ||||
| 		if (ts->in_file) | ||||
| 			fput(ts->in_file); | ||||
| 
 | ||||
| 		if (ts->out_file) | ||||
| 			fput(ts->out_file); | ||||
| 
 | ||||
| 		kfree(ts); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	trans->priv = ts; | ||||
| 	trans->status = Connected; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_fd_close - shutdown file descriptor | ||||
|  * @trans: private socket structure | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static void v9fs_fd_close(struct v9fs_transport *trans) | ||||
| { | ||||
| 	struct v9fs_trans_fd *ts; | ||||
| 
 | ||||
| 	if (!trans) | ||||
| 		return; | ||||
| 
 | ||||
| 	trans->status = Disconnected; | ||||
| 	ts = trans->priv; | ||||
| 
 | ||||
| 	if (!ts) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (ts->in_file) | ||||
| 		fput(ts->in_file); | ||||
| 
 | ||||
| 	if (ts->out_file) | ||||
| 		fput(ts->out_file); | ||||
| 
 | ||||
| 	kfree(ts); | ||||
| } | ||||
| 
 | ||||
| struct v9fs_transport v9fs_trans_fd = { | ||||
| 	.init = v9fs_fd_init, | ||||
| 	.write = v9fs_fd_send, | ||||
| 	.read = v9fs_fd_recv, | ||||
| 	.close = v9fs_fd_close, | ||||
| }; | ||||
| 
 | ||||
							
								
								
									
										282
									
								
								fs/9p/trans_sock.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								fs/9p/trans_sock.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,282 @@ | ||||
| /*
 | ||||
|  * linux/fs/9p/trans_socket.c | ||||
|  * | ||||
|  * Socket Transport Layer | ||||
|  * | ||||
|  *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||||
|  *  Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com> | ||||
|  *  Copyright (C) 1995, 1996 by Olaf Kirch <okir@monad.swb.de> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to: | ||||
|  *  Free Software Foundation | ||||
|  *  51 Franklin Street, Fifth Floor | ||||
|  *  Boston, MA  02111-1301  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/config.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/net.h> | ||||
| #include <linux/ipv6.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/un.h> | ||||
| #include <asm/uaccess.h> | ||||
| #include <linux/inet.h> | ||||
| #include <linux/idr.h> | ||||
| 
 | ||||
| #include "debug.h" | ||||
| #include "v9fs.h" | ||||
| #include "transport.h" | ||||
| 
 | ||||
| #define V9FS_PORT 564 | ||||
| 
 | ||||
| struct v9fs_trans_sock { | ||||
| 	struct socket *s; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_sock_recv - receive from a socket | ||||
|  * @v9ses: session information | ||||
|  * @v: buffer to receive data into | ||||
|  * @len: size of receive buffer | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static int v9fs_sock_recv(struct v9fs_transport *trans, void *v, int len) | ||||
| { | ||||
| 	struct msghdr msg; | ||||
| 	struct kvec iov; | ||||
| 	int result; | ||||
| 	mm_segment_t oldfs; | ||||
| 	struct v9fs_trans_sock *ts = trans ? trans->priv : NULL; | ||||
| 
 | ||||
| 	if (trans->status == Disconnected) | ||||
| 		return -EREMOTEIO; | ||||
| 
 | ||||
| 	result = -EINVAL; | ||||
| 
 | ||||
| 	oldfs = get_fs(); | ||||
| 	set_fs(get_ds()); | ||||
| 
 | ||||
| 	iov.iov_base = v; | ||||
| 	iov.iov_len = len; | ||||
| 	msg.msg_name = NULL; | ||||
| 	msg.msg_namelen = 0; | ||||
| 	msg.msg_iovlen = 1; | ||||
| 	msg.msg_control = NULL; | ||||
| 	msg.msg_controllen = 0; | ||||
| 	msg.msg_namelen = 0; | ||||
| 	msg.msg_flags = MSG_NOSIGNAL; | ||||
| 
 | ||||
| 	result = kernel_recvmsg(ts->s, &msg, &iov, 1, len, 0); | ||||
| 
 | ||||
| 	dprintk(DEBUG_TRANS, "socket state %d\n", ts->s->state); | ||||
| 	set_fs(oldfs); | ||||
| 
 | ||||
| 	if (result <= 0) { | ||||
| 		if (result != -ERESTARTSYS) | ||||
| 			trans->status = Disconnected; | ||||
| 	} | ||||
| 
 | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_sock_send - send to a socket | ||||
|  * @v9ses: session information | ||||
|  * @v: buffer to send data from | ||||
|  * @len: size of send buffer | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static int v9fs_sock_send(struct v9fs_transport *trans, void *v, int len) | ||||
| { | ||||
| 	struct kvec iov; | ||||
| 	struct msghdr msg; | ||||
| 	int result = -1; | ||||
| 	mm_segment_t oldfs; | ||||
| 	struct v9fs_trans_sock *ts = trans ? trans->priv : NULL; | ||||
| 
 | ||||
| 	dprintk(DEBUG_TRANS, "Sending packet size %d (%x)\n", len, len); | ||||
| 	dump_data(v, len); | ||||
| 
 | ||||
| 	down(&trans->writelock); | ||||
| 
 | ||||
| 	oldfs = get_fs(); | ||||
| 	set_fs(get_ds()); | ||||
| 	iov.iov_base = v; | ||||
| 	iov.iov_len = len; | ||||
| 	msg.msg_name = NULL; | ||||
| 	msg.msg_namelen = 0; | ||||
| 	msg.msg_iovlen = 1; | ||||
| 	msg.msg_control = NULL; | ||||
| 	msg.msg_controllen = 0; | ||||
| 	msg.msg_namelen = 0; | ||||
| 	msg.msg_flags = MSG_NOSIGNAL; | ||||
| 	result = kernel_sendmsg(ts->s, &msg, &iov, 1, len); | ||||
| 	set_fs(oldfs); | ||||
| 
 | ||||
| 	if (result < 0) { | ||||
| 		if (result != -ERESTARTSYS) | ||||
| 			trans->status = Disconnected; | ||||
| 	} | ||||
| 
 | ||||
| 	up(&trans->writelock); | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_tcp_init - initialize TCP socket | ||||
|  * @v9ses: session information | ||||
|  * @addr: address of server to mount | ||||
|  * @data: mount options | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static int | ||||
| v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, char *data) | ||||
| { | ||||
| 	struct socket *csocket = NULL; | ||||
| 	struct sockaddr_in sin_server; | ||||
| 	int rc = 0; | ||||
| 	struct v9fs_trans_sock *ts = NULL; | ||||
| 	struct v9fs_transport *trans = v9ses->transport; | ||||
| 
 | ||||
| 	sema_init(&trans->writelock, 1); | ||||
| 	sema_init(&trans->readlock, 1); | ||||
| 
 | ||||
| 	ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL); | ||||
| 
 | ||||
| 	if (!ts) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	trans->priv = ts; | ||||
| 	ts->s = NULL; | ||||
| 
 | ||||
| 	if (!addr) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	dprintk(DEBUG_TRANS, "Connecting to %s\n", addr); | ||||
| 
 | ||||
| 	sin_server.sin_family = AF_INET; | ||||
| 	sin_server.sin_addr.s_addr = in_aton(addr); | ||||
| 	sin_server.sin_port = htons(v9ses->port); | ||||
| 	sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket); | ||||
| 	rc = csocket->ops->connect(csocket, | ||||
| 				   (struct sockaddr *)&sin_server, | ||||
| 				   sizeof(struct sockaddr_in), 0); | ||||
| 	if (rc < 0) { | ||||
| 		eprintk(KERN_ERR, | ||||
| 			"v9fs_trans_tcp: problem connecting socket to %s\n", | ||||
| 			addr); | ||||
| 		return rc; | ||||
| 	} | ||||
| 	csocket->sk->sk_allocation = GFP_NOIO; | ||||
| 	ts->s = csocket; | ||||
| 	trans->status = Connected; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_unix_init - initialize UNIX domain socket | ||||
|  * @v9ses: session information | ||||
|  * @dev_name: path to named pipe | ||||
|  * @data: mount options | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static int | ||||
| v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name, | ||||
| 	       char *data) | ||||
| { | ||||
| 	int rc; | ||||
| 	struct socket *csocket; | ||||
| 	struct sockaddr_un sun_server; | ||||
| 	struct v9fs_transport *trans; | ||||
| 	struct v9fs_trans_sock *ts; | ||||
| 
 | ||||
| 	rc = 0; | ||||
| 	csocket = NULL; | ||||
| 	trans = v9ses->transport; | ||||
| 
 | ||||
| 	if (strlen(dev_name) > UNIX_PATH_MAX) { | ||||
| 		eprintk(KERN_ERR, "v9fs_trans_unix: address too long: %s\n", | ||||
| 			dev_name); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL); | ||||
| 	if (!ts) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	trans->priv = ts; | ||||
| 	ts->s = NULL; | ||||
| 
 | ||||
| 	sema_init(&trans->writelock, 1); | ||||
| 	sema_init(&trans->readlock, 1); | ||||
| 
 | ||||
| 	sun_server.sun_family = PF_UNIX; | ||||
| 	strcpy(sun_server.sun_path, dev_name); | ||||
| 	sock_create_kern(PF_UNIX, SOCK_STREAM, 0, &csocket); | ||||
| 	rc = csocket->ops->connect(csocket, (struct sockaddr *)&sun_server, | ||||
| 		sizeof(struct sockaddr_un) - 1, 0);	/* -1 *is* important */ | ||||
| 	if (rc < 0) { | ||||
| 		eprintk(KERN_ERR, | ||||
| 			"v9fs_trans_unix: problem connecting socket: %s: %d\n", | ||||
| 			dev_name, rc); | ||||
| 		return rc; | ||||
| 	} | ||||
| 	csocket->sk->sk_allocation = GFP_NOIO; | ||||
| 	ts->s = csocket; | ||||
| 	trans->status = Connected; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * v9fs_sock_close - shutdown socket | ||||
|  * @trans: private socket structure | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static void v9fs_sock_close(struct v9fs_transport *trans) | ||||
| { | ||||
| 	struct v9fs_trans_sock *ts = trans ? trans->priv : NULL; | ||||
| 
 | ||||
| 	if ((ts) && (ts->s)) { | ||||
| 		dprintk(DEBUG_TRANS, "closing the socket %p\n", ts->s); | ||||
| 		sock_release(ts->s); | ||||
| 		ts->s = NULL; | ||||
| 		trans->status = Disconnected; | ||||
| 		dprintk(DEBUG_TRANS, "socket closed\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	kfree(ts); | ||||
| } | ||||
| 
 | ||||
| struct v9fs_transport v9fs_trans_tcp = { | ||||
| 	.init = v9fs_tcp_init, | ||||
| 	.write = v9fs_sock_send, | ||||
| 	.read = v9fs_sock_recv, | ||||
| 	.close = v9fs_sock_close, | ||||
| }; | ||||
| 
 | ||||
| struct v9fs_transport v9fs_trans_unix = { | ||||
| 	.init = v9fs_unix_init, | ||||
| 	.write = v9fs_sock_send, | ||||
| 	.read = v9fs_sock_recv, | ||||
| 	.close = v9fs_sock_close, | ||||
| }; | ||||
							
								
								
									
										46
									
								
								fs/9p/transport.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								fs/9p/transport.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| /*
 | ||||
|  * linux/fs/9p/transport.h | ||||
|  * | ||||
|  * Transport Definition | ||||
|  * | ||||
|  *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to: | ||||
|  *  Free Software Foundation | ||||
|  *  51 Franklin Street, Fifth Floor | ||||
|  *  Boston, MA  02111-1301  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| enum v9fs_transport_status { | ||||
| 	Connected, | ||||
| 	Disconnected, | ||||
| 	Hung, | ||||
| }; | ||||
| 
 | ||||
| struct v9fs_transport { | ||||
| 	enum v9fs_transport_status status; | ||||
| 	struct semaphore writelock; | ||||
| 	struct semaphore readlock; | ||||
| 	void *priv; | ||||
| 
 | ||||
| 	int (*init) (struct v9fs_session_info *, const char *, char *); | ||||
| 	int (*write) (struct v9fs_transport *, void *, int); | ||||
| 	int (*read) (struct v9fs_transport *, void *, int); | ||||
| 	void (*close) (struct v9fs_transport *); | ||||
| }; | ||||
| 
 | ||||
| extern struct v9fs_transport v9fs_trans_tcp; | ||||
| extern struct v9fs_transport v9fs_trans_unix; | ||||
| extern struct v9fs_transport v9fs_trans_fd; | ||||
| @ -296,11 +296,6 @@ v9fs_session_init(struct v9fs_session_info *v9ses, | ||||
| 	case PROTO_FD: | ||||
| 		trans_proto = &v9fs_trans_fd; | ||||
| 		*v9ses->remotename = 0; | ||||
| 		if((v9ses->wfdno == ~0) || (v9ses->rfdno == ~0)) { | ||||
| 			printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n"); | ||||
| 			retval = -ENOPROTOOPT; | ||||
| 			goto SessCleanUp; | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		printk(KERN_ERR "v9fs: Bad mount protocol %d\n", v9ses->proto); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user