1 /* 2 * Copyright (c) 2000-2001 Christoph Hellwig. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions, and the following disclaimer, 10 * without modification. 11 * 2. The name of the author may not be used to endorse or promote products 12 * derived from this software without specific prior written permission. 13 * 14 * Alternatively, this software may be distributed under the terms of the 15 * GNU General Public License ("GPL"). 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 /* 31 * Veritas filesystem driver - lookup and other directory related code. 32 */ 33 #include <linux/fs.h> 34 #include <linux/time.h> 35 #include <linux/mm.h> 36 #include <linux/highmem.h> 37 #include <linux/kernel.h> 38 #include <linux/pagemap.h> 39 #include <linux/smp_lock.h> 40 41 #include "vxfs.h" 42 #include "vxfs_dir.h" 43 #include "vxfs_inode.h" 44 #include "vxfs_extern.h" 45 46 /* 47 * Number of VxFS blocks per page. 48 */ 49 #define VXFS_BLOCK_PER_PAGE(sbp) ((PAGE_CACHE_SIZE / (sbp)->s_blocksize)) 50 51 52 static struct dentry * vxfs_lookup(struct inode *, struct dentry *, struct nameidata *); 53 static int vxfs_readdir(struct file *, void *, filldir_t); 54 55 struct inode_operations vxfs_dir_inode_ops = { 56 .lookup = vxfs_lookup, 57 }; 58 59 struct file_operations vxfs_dir_operations = { 60 .readdir = vxfs_readdir, 61 }; 62 63 64 static __inline__ u_long 65 dir_pages(struct inode *inode) 66 { 67 return (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 68 } 69 70 static __inline__ u_long 71 dir_blocks(struct inode *ip) 72 { 73 u_long bsize = ip->i_sb->s_blocksize; 74 return (ip->i_size + bsize - 1) & ~(bsize - 1); 75 } 76 77 /* 78 * NOTE! unlike strncmp, vxfs_match returns 1 for success, 0 for failure. 79 * 80 * len <= VXFS_NAMELEN and de != NULL are guaranteed by caller. 81 */ 82 static __inline__ int 83 vxfs_match(int len, const char * const name, struct vxfs_direct *de) 84 { 85 if (len != de->d_namelen) 86 return 0; 87 if (!de->d_ino) 88 return 0; 89 return !memcmp(name, de->d_name, len); 90 } 91 92 static __inline__ struct vxfs_direct * 93 vxfs_next_entry(struct vxfs_direct *de) 94 { 95 return ((struct vxfs_direct *)((char*)de + de->d_reclen)); 96 } 97 98 /** 99 * vxfs_find_entry - find a mathing directory entry for a dentry 100 * @ip: directory inode 101 * @dp: dentry for which we want to find a direct 102 * @ppp: gets filled with the page the return value sits in 103 * 104 * Description: 105 * vxfs_find_entry finds a &struct vxfs_direct for the VFS directory 106 * cache entry @dp. @ppp will be filled with the page the return 107 * value resides in. 108 * 109 * Returns: 110 * The wanted direct on success, else a NULL pointer. 111 */ 112 static struct vxfs_direct * 113 vxfs_find_entry(struct inode *ip, struct dentry *dp, struct page **ppp) 114 { 115 u_long npages, page, nblocks, pblocks, block; 116 u_long bsize = ip->i_sb->s_blocksize; 117 const char *name = dp->d_name.name; 118 int namelen = dp->d_name.len; 119 120 npages = dir_pages(ip); 121 nblocks = dir_blocks(ip); 122 pblocks = VXFS_BLOCK_PER_PAGE(ip->i_sb); 123 124 for (page = 0; page < npages; page++) { 125 caddr_t kaddr; 126 struct page *pp; 127 128 pp = vxfs_get_page(ip->i_mapping, page); 129 if (IS_ERR(pp)) 130 continue; 131 kaddr = (caddr_t)page_address(pp); 132 133 for (block = 0; block <= nblocks && block <= pblocks; block++) { 134 caddr_t baddr, limit; 135 struct vxfs_dirblk *dbp; 136 struct vxfs_direct *de; 137 138 baddr = kaddr + (block * bsize); 139 limit = baddr + bsize - VXFS_DIRLEN(1); 140 141 dbp = (struct vxfs_dirblk *)baddr; 142 de = (struct vxfs_direct *)(baddr + VXFS_DIRBLKOV(dbp)); 143 144 for (; (caddr_t)de <= limit; de = vxfs_next_entry(de)) { 145 if (!de->d_reclen) 146 break; 147 if (!de->d_ino) 148 continue; 149 if (vxfs_match(namelen, name, de)) { 150 *ppp = pp; 151 return (de); 152 } 153 } 154 } 155 vxfs_put_page(pp); 156 } 157 158 return NULL; 159 } 160 161 /** 162 * vxfs_inode_by_name - find inode number for dentry 163 * @dip: directory to search in 164 * @dp: dentry we seach for 165 * 166 * Description: 167 * vxfs_inode_by_name finds out the inode number of 168 * the path component described by @dp in @dip. 169 * 170 * Returns: 171 * The wanted inode number on success, else Zero. 172 */ 173 static ino_t 174 vxfs_inode_by_name(struct inode *dip, struct dentry *dp) 175 { 176 struct vxfs_direct *de; 177 struct page *pp; 178 ino_t ino = 0; 179 180 de = vxfs_find_entry(dip, dp, &pp); 181 if (de) { 182 ino = de->d_ino; 183 kunmap(pp); 184 page_cache_release(pp); 185 } 186 187 return (ino); 188 } 189 190 /** 191 * vxfs_lookup - lookup pathname component 192 * @dip: dir in which we lookup 193 * @dp: dentry we lookup 194 * @nd: lookup nameidata 195 * 196 * Description: 197 * vxfs_lookup tries to lookup the pathname component described 198 * by @dp in @dip. 199 * 200 * Returns: 201 * A NULL-pointer on success, else an negative error code encoded 202 * in the return pointer. 203 */ 204 static struct dentry * 205 vxfs_lookup(struct inode *dip, struct dentry *dp, struct nameidata *nd) 206 { 207 struct inode *ip = NULL; 208 ino_t ino; 209 210 if (dp->d_name.len > VXFS_NAMELEN) 211 return ERR_PTR(-ENAMETOOLONG); 212 213 lock_kernel(); 214 ino = vxfs_inode_by_name(dip, dp); 215 if (ino) { 216 ip = iget(dip->i_sb, ino); 217 if (!ip) { 218 unlock_kernel(); 219 return ERR_PTR(-EACCES); 220 } 221 } 222 unlock_kernel(); 223 d_add(dp, ip); 224 return NULL; 225 } 226 227 /** 228 * vxfs_readdir - read a directory 229 * @fp: the directory to read 230 * @retp: return buffer 231 * @filler: filldir callback 232 * 233 * Description: 234 * vxfs_readdir fills @retp with directory entries from @fp 235 * using the VFS supplied callback @filler. 236 * 237 * Returns: 238 * Zero. 239 */ 240 static int 241 vxfs_readdir(struct file *fp, void *retp, filldir_t filler) 242 { 243 struct inode *ip = fp->f_dentry->d_inode; 244 struct super_block *sbp = ip->i_sb; 245 u_long bsize = sbp->s_blocksize; 246 u_long page, npages, block, pblocks, nblocks, offset; 247 loff_t pos; 248 249 switch ((long)fp->f_pos) { 250 case 0: 251 if (filler(retp, ".", 1, fp->f_pos, ip->i_ino, DT_DIR) < 0) 252 goto out; 253 fp->f_pos++; 254 /* fallthrough */ 255 case 1: 256 if (filler(retp, "..", 2, fp->f_pos, VXFS_INO(ip)->vii_dotdot, DT_DIR) < 0) 257 goto out; 258 fp->f_pos++; 259 /* fallthrough */ 260 } 261 262 pos = fp->f_pos - 2; 263 264 if (pos > VXFS_DIRROUND(ip->i_size)) { 265 unlock_kernel(); 266 return 0; 267 } 268 269 npages = dir_pages(ip); 270 nblocks = dir_blocks(ip); 271 pblocks = VXFS_BLOCK_PER_PAGE(sbp); 272 273 page = pos >> PAGE_CACHE_SHIFT; 274 offset = pos & ~PAGE_CACHE_MASK; 275 block = (u_long)(pos >> sbp->s_blocksize_bits) % pblocks; 276 277 for (; page < npages; page++, block = 0) { 278 caddr_t kaddr; 279 struct page *pp; 280 281 pp = vxfs_get_page(ip->i_mapping, page); 282 if (IS_ERR(pp)) 283 continue; 284 kaddr = (caddr_t)page_address(pp); 285 286 for (; block <= nblocks && block <= pblocks; block++) { 287 caddr_t baddr, limit; 288 struct vxfs_dirblk *dbp; 289 struct vxfs_direct *de; 290 291 baddr = kaddr + (block * bsize); 292 limit = baddr + bsize - VXFS_DIRLEN(1); 293 294 dbp = (struct vxfs_dirblk *)baddr; 295 de = (struct vxfs_direct *) 296 (offset ? 297 (kaddr + offset) : 298 (baddr + VXFS_DIRBLKOV(dbp))); 299 300 for (; (caddr_t)de <= limit; de = vxfs_next_entry(de)) { 301 int over; 302 303 if (!de->d_reclen) 304 break; 305 if (!de->d_ino) 306 continue; 307 308 offset = (caddr_t)de - kaddr; 309 over = filler(retp, de->d_name, de->d_namelen, 310 ((page << PAGE_CACHE_SHIFT) | offset) + 2, 311 de->d_ino, DT_UNKNOWN); 312 if (over) { 313 vxfs_put_page(pp); 314 goto done; 315 } 316 } 317 offset = 0; 318 } 319 vxfs_put_page(pp); 320 offset = 0; 321 } 322 323 done: 324 fp->f_pos = ((page << PAGE_CACHE_SHIFT) | offset) + 2; 325 out: 326 unlock_kernel(); 327 return 0; 328 } 329