xref: /linux/fs/9p/fid.c (revision 87c2ce3b9305b9b723faeedf6e32ef703ec9b33a)
1 /*
2  * V9FS FID Management
3  *
4  *  Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to:
18  *  Free Software Foundation
19  *  51 Franklin Street, Fifth Floor
20  *  Boston, MA  02111-1301  USA
21  *
22  */
23 
24 #include <linux/config.h>
25 #include <linux/module.h>
26 #include <linux/errno.h>
27 #include <linux/fs.h>
28 #include <linux/idr.h>
29 
30 #include "debug.h"
31 #include "v9fs.h"
32 #include "9p.h"
33 #include "v9fs_vfs.h"
34 #include "fid.h"
35 
36 /**
37  * v9fs_fid_insert - add a fid to a dentry
38  * @fid: fid to add
39  * @dentry: dentry that it is being added to
40  *
41  */
42 
43 static int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry)
44 {
45 	struct list_head *fid_list = (struct list_head *)dentry->d_fsdata;
46 	dprintk(DEBUG_9P, "fid %d (%p) dentry %s (%p)\n", fid->fid, fid,
47 		dentry->d_iname, dentry);
48 	if (dentry->d_fsdata == NULL) {
49 		dentry->d_fsdata =
50 		    kmalloc(sizeof(struct list_head), GFP_KERNEL);
51 		if (dentry->d_fsdata == NULL) {
52 			dprintk(DEBUG_ERROR, "Out of memory\n");
53 			return -ENOMEM;
54 		}
55 		fid_list = (struct list_head *)dentry->d_fsdata;
56 		INIT_LIST_HEAD(fid_list);	/* Initialize list head */
57 	}
58 
59 	fid->uid = current->uid;
60 	fid->pid = current->pid;
61 	list_add(&fid->list, fid_list);
62 	return 0;
63 }
64 
65 /**
66  * v9fs_fid_create - allocate a FID structure
67  * @dentry - dentry to link newly created fid to
68  *
69  */
70 
71 struct v9fs_fid *v9fs_fid_create(struct dentry *dentry,
72 	struct v9fs_session_info *v9ses, int fid, int create)
73 {
74 	struct v9fs_fid *new;
75 
76 	dprintk(DEBUG_9P, "fid create dentry %p, fid %d, create %d\n",
77 		dentry, fid, create);
78 
79 	new = kmalloc(sizeof(struct v9fs_fid), GFP_KERNEL);
80 	if (new == NULL) {
81 		dprintk(DEBUG_ERROR, "Out of Memory\n");
82 		return ERR_PTR(-ENOMEM);
83 	}
84 
85 	new->fid = fid;
86 	new->v9ses = v9ses;
87 	new->fidopen = 0;
88 	new->fidcreate = create;
89 	new->fidclunked = 0;
90 	new->iounit = 0;
91 	new->rdir_pos = 0;
92 	new->rdir_fcall = NULL;
93 
94 	if (v9fs_fid_insert(new, dentry) == 0)
95 		return new;
96 	else {
97 		dprintk(DEBUG_ERROR, "Problems inserting to dentry\n");
98 		kfree(new);
99 		return NULL;
100 	}
101 }
102 
103 /**
104  * v9fs_fid_destroy - deallocate a FID structure
105  * @fid: fid to destroy
106  *
107  */
108 
109 void v9fs_fid_destroy(struct v9fs_fid *fid)
110 {
111 	list_del(&fid->list);
112 	kfree(fid);
113 }
114 
115 /**
116  * v9fs_fid_walk_up - walks from the process current directory
117  * 	up to the specified dentry.
118  */
119 static struct v9fs_fid *v9fs_fid_walk_up(struct dentry *dentry)
120 {
121 	int fidnum, cfidnum, err;
122 	struct v9fs_fid *cfid;
123 	struct dentry *cde;
124 	struct v9fs_session_info *v9ses;
125 
126 	v9ses = v9fs_inode2v9ses(current->fs->pwd->d_inode);
127 	cfid = v9fs_fid_lookup(current->fs->pwd);
128 	if (cfid == NULL) {
129 		dprintk(DEBUG_ERROR, "process cwd doesn't have a fid\n");
130 		return ERR_PTR(-ENOENT);
131 	}
132 
133 	cfidnum = cfid->fid;
134 	cde = current->fs->pwd;
135 	/* TODO: take advantage of multiwalk */
136 
137 	fidnum = v9fs_get_idpool(&v9ses->fidpool);
138 	if (fidnum < 0) {
139 		dprintk(DEBUG_ERROR, "could not get a new fid num\n");
140 		err = -ENOENT;
141 		goto clunk_fid;
142 	}
143 
144 	while (cde != dentry) {
145 		if (cde == cde->d_parent) {
146 			dprintk(DEBUG_ERROR, "can't find dentry\n");
147 			err = -ENOENT;
148 			goto clunk_fid;
149 		}
150 
151 		err = v9fs_t_walk(v9ses, cfidnum, fidnum, "..", NULL);
152 		if (err < 0) {
153 			dprintk(DEBUG_ERROR, "problem walking to parent\n");
154 			goto clunk_fid;
155 		}
156 
157 		cfidnum = fidnum;
158 		cde = cde->d_parent;
159 	}
160 
161 	return v9fs_fid_create(dentry, v9ses, fidnum, 0);
162 
163 clunk_fid:
164 	v9fs_t_clunk(v9ses, fidnum);
165 	return ERR_PTR(err);
166 }
167 
168 /**
169  * v9fs_fid_lookup - retrieve the right fid from a  particular dentry
170  * @dentry: dentry to look for fid in
171  * @type: intent of lookup (operation or traversal)
172  *
173  * search list of fids associated with a dentry for a fid with a matching
174  * thread id or uid.  If that fails, look up the dentry's parents to see if you
175  * can find a matching fid.
176  *
177  */
178 
179 struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry)
180 {
181 	struct list_head *fid_list = (struct list_head *)dentry->d_fsdata;
182 	struct v9fs_fid *current_fid = NULL;
183 	struct v9fs_fid *temp = NULL;
184 	struct v9fs_fid *return_fid = NULL;
185 
186 	dprintk(DEBUG_9P, " dentry: %s (%p)\n", dentry->d_iname, dentry);
187 
188 	if (fid_list) {
189 		list_for_each_entry_safe(current_fid, temp, fid_list, list) {
190 			if (!current_fid->fidcreate) {
191 				return_fid = current_fid;
192 				break;
193 			}
194 		}
195 
196 		if (!return_fid)
197 			return_fid = current_fid;
198 	}
199 
200 	/* we are at the root but didn't match */
201 	if ((!return_fid) && (dentry->d_parent == dentry)) {
202 		/* TODO: clone attach with new uid */
203 		return_fid = current_fid;
204 	}
205 
206 	if (!return_fid) {
207 		struct dentry *par = current->fs->pwd->d_parent;
208 		int count = 1;
209 		while (par != NULL) {
210 			if (par == dentry)
211 				break;
212 			count++;
213 			if (par == par->d_parent) {
214 				dprintk(DEBUG_ERROR,
215 					"got to root without finding dentry\n");
216 				break;
217 			}
218 			par = par->d_parent;
219 		}
220 
221 /* XXX - there may be some duplication we can get rid of */
222 		if (par == dentry) {
223 			return_fid = v9fs_fid_walk_up(dentry);
224 			if (IS_ERR(return_fid))
225 				return_fid = NULL;
226 		}
227 	}
228 
229 	return return_fid;
230 }
231 
232 struct v9fs_fid *v9fs_fid_get_created(struct dentry *dentry)
233 {
234 	struct list_head *fid_list;
235 	struct v9fs_fid *fid, *ftmp, *ret;
236 
237 	dprintk(DEBUG_9P, " dentry: %s (%p)\n", dentry->d_iname, dentry);
238 	fid_list = (struct list_head *)dentry->d_fsdata;
239 	ret = NULL;
240 	if (fid_list) {
241 		list_for_each_entry_safe(fid, ftmp, fid_list, list) {
242 			if (fid->fidcreate && fid->pid == current->pid) {
243 				list_del(&fid->list);
244 				ret = fid;
245 				break;
246 			}
247 		}
248 	}
249 
250 	dprintk(DEBUG_9P, "return %p\n", ret);
251 	return ret;
252 }
253