1 /* 2 * V9FS FID Management 3 * 4 * Copyright (C) 2005, 2006 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 version 2 8 * as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to: 17 * Free Software Foundation 18 * 51 Franklin Street, Fifth Floor 19 * Boston, MA 02111-1301 USA 20 * 21 */ 22 23 #include <linux/module.h> 24 #include <linux/errno.h> 25 #include <linux/fs.h> 26 #include <linux/sched.h> 27 #include <linux/idr.h> 28 #include <asm/semaphore.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 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 list_add(&fid->list, fid_list); 61 return 0; 62 } 63 64 /** 65 * v9fs_fid_create - allocate a FID structure 66 * @dentry - dentry to link newly created fid to 67 * 68 */ 69 70 struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *v9ses, int fid) 71 { 72 struct v9fs_fid *new; 73 74 dprintk(DEBUG_9P, "fid create fid %d\n", fid); 75 new = kmalloc(sizeof(struct v9fs_fid), GFP_KERNEL); 76 if (new == NULL) { 77 dprintk(DEBUG_ERROR, "Out of Memory\n"); 78 return ERR_PTR(-ENOMEM); 79 } 80 81 new->fid = fid; 82 new->v9ses = v9ses; 83 new->fidopen = 0; 84 new->fidclunked = 0; 85 new->iounit = 0; 86 new->rdir_pos = 0; 87 new->rdir_fcall = NULL; 88 init_MUTEX(&new->lock); 89 INIT_LIST_HEAD(&new->list); 90 91 return new; 92 } 93 94 /** 95 * v9fs_fid_destroy - deallocate a FID structure 96 * @fid: fid to destroy 97 * 98 */ 99 100 void v9fs_fid_destroy(struct v9fs_fid *fid) 101 { 102 list_del(&fid->list); 103 kfree(fid); 104 } 105 106 /** 107 * v9fs_fid_lookup - return a locked fid from a dentry 108 * @dentry: dentry to look for fid in 109 * 110 * find a fid in the dentry, obtain its semaphore and return a reference to it. 111 * code calling lookup is responsible for releasing lock 112 * 113 * TODO: only match fids that have the same uid as current user 114 * 115 */ 116 117 struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry) 118 { 119 struct list_head *fid_list = (struct list_head *)dentry->d_fsdata; 120 struct v9fs_fid *return_fid = NULL; 121 122 dprintk(DEBUG_9P, " dentry: %s (%p)\n", dentry->d_iname, dentry); 123 124 if (fid_list) 125 return_fid = list_entry(fid_list->next, struct v9fs_fid, list); 126 127 if (!return_fid) { 128 dprintk(DEBUG_ERROR, "Couldn't find a fid in dentry\n"); 129 return_fid = ERR_PTR(-EBADF); 130 } 131 132 if(down_interruptible(&return_fid->lock)) 133 return ERR_PTR(-EINTR); 134 135 return return_fid; 136 } 137 138 /** 139 * v9fs_fid_clone - lookup the fid for a dentry, clone a private copy and 140 * release it 141 * @dentry: dentry to look for fid in 142 * 143 * find a fid in the dentry and then clone to a new private fid 144 * 145 * TODO: only match fids that have the same uid as current user 146 * 147 */ 148 149 struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry) 150 { 151 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); 152 struct v9fs_fid *base_fid, *new_fid = ERR_PTR(-EBADF); 153 struct v9fs_fcall *fcall = NULL; 154 int fid, err; 155 156 base_fid = v9fs_fid_lookup(dentry); 157 158 if(IS_ERR(base_fid)) 159 return base_fid; 160 161 if(base_fid) { /* clone fid */ 162 fid = v9fs_get_idpool(&v9ses->fidpool); 163 if (fid < 0) { 164 eprintk(KERN_WARNING, "newfid fails!\n"); 165 new_fid = ERR_PTR(-ENOSPC); 166 goto Release_Fid; 167 } 168 169 err = v9fs_t_walk(v9ses, base_fid->fid, fid, NULL, &fcall); 170 if (err < 0) { 171 dprintk(DEBUG_ERROR, "clone walk didn't work\n"); 172 v9fs_put_idpool(fid, &v9ses->fidpool); 173 new_fid = ERR_PTR(err); 174 goto Free_Fcall; 175 } 176 new_fid = v9fs_fid_create(v9ses, fid); 177 if (new_fid == NULL) { 178 dprintk(DEBUG_ERROR, "out of memory\n"); 179 new_fid = ERR_PTR(-ENOMEM); 180 } 181 Free_Fcall: 182 kfree(fcall); 183 } 184 185 Release_Fid: 186 up(&base_fid->lock); 187 return new_fid; 188 } 189 190 void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid) 191 { 192 v9fs_t_clunk(v9ses, fid->fid); 193 v9fs_fid_destroy(fid); 194 } 195