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 52static struct dentry * vxfs_lookup(struct inode *, struct dentry *, struct nameidata *); 53static int vxfs_readdir(struct file *, void *, filldir_t); 54 55const struct inode_operations vxfs_dir_inode_ops = { 56 .lookup = vxfs_lookup, 57}; 58 59const struct file_operations vxfs_dir_operations = { 60 .llseek = generic_file_llseek, 61 .read = generic_read_dir, 62 .readdir = vxfs_readdir, 63}; 64 65 66static inline u_long 67dir_pages(struct inode *inode) 68{ 69 return (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 70} 71 72static inline u_long 73dir_blocks(struct inode *ip) 74{ 75 u_long bsize = ip->i_sb->s_blocksize; 76 return (ip->i_size + bsize - 1) & ~(bsize - 1); 77} 78 79/* 80 * NOTE! unlike strncmp, vxfs_match returns 1 for success, 0 for failure. 81 * 82 * len <= VXFS_NAMELEN and de != NULL are guaranteed by caller. 83 */ 84static inline int 85vxfs_match(int len, const char * const name, struct vxfs_direct *de) 86{ 87 if (len != de->d_namelen) 88 return 0; 89 if (!de->d_ino) 90 return 0; 91 return !memcmp(name, de->d_name, len); 92} 93 94static inline struct vxfs_direct * 95vxfs_next_entry(struct vxfs_direct *de) 96{ 97 return ((struct vxfs_direct *)((char*)de + de->d_reclen)); 98} 99 100/** 101 * vxfs_find_entry - find a mathing directory entry for a dentry 102 * @ip: directory inode 103 * @dp: dentry for which we want to find a direct 104 * @ppp: gets filled with the page the return value sits in 105 * 106 * Description: 107 * vxfs_find_entry finds a &struct vxfs_direct for the VFS directory 108 * cache entry @dp. @ppp will be filled with the page the return 109 * value resides in. 110 * 111 * Returns: 112 * The wanted direct on success, else a NULL pointer. 113 */ 114static struct vxfs_direct * 115vxfs_find_entry(struct inode *ip, struct dentry *dp, struct page **ppp) 116{ 117 u_long npages, page, nblocks, pblocks, block; 118 u_long bsize = ip->i_sb->s_blocksize; 119 const char *name = dp->d_name.name; 120 int namelen = dp->d_name.len; 121 122 npages = dir_pages(ip); 123 nblocks = dir_blocks(ip); 124 pblocks = VXFS_BLOCK_PER_PAGE(ip->i_sb); 125 126 for (page = 0; page < npages; page++) { 127 caddr_t kaddr; 128 struct page *pp; 129 130 pp = vxfs_get_page(ip->i_mapping, page); 131 if (IS_ERR(pp)) 132 continue; 133 kaddr = (caddr_t)page_address(pp); 134 135 for (block = 0; block <= nblocks && block <= pblocks; block++) { 136 caddr_t baddr, limit; 137 struct vxfs_dirblk *dbp; 138 struct vxfs_direct *de; 139 140 baddr = kaddr + (block * bsize); 141 limit = baddr + bsize - VXFS_DIRLEN(1); 142 143 dbp = (struct vxfs_dirblk *)baddr; 144 de = (struct vxfs_direct *)(baddr + VXFS_DIRBLKOV(dbp)); 145 146 for (; (caddr_t)de <= limit; de = vxfs_next_entry(de)) { 147 if (!de->d_reclen) 148 break; 149 if (!de->d_ino) 150 continue; 151 if (vxfs_match(namelen, name, de)) { 152 *ppp = pp; 153 return (de); 154 } 155 } 156 } 157 vxfs_put_page(pp); 158 } 159 160 return NULL; 161} 162 163/** 164 * vxfs_inode_by_name - find inode number for dentry 165 * @dip: directory to search in 166 * @dp: dentry we seach for 167 * 168 * Description: 169 * vxfs_inode_by_name finds out the inode number of 170 * the path component described by @dp in @dip. 171 * 172 * Returns: 173 * The wanted inode number on success, else Zero. 174 */ 175static ino_t 176vxfs_inode_by_name(struct inode *dip, struct dentry *dp) 177{ 178 struct vxfs_direct *de; 179 struct page *pp; 180 ino_t ino = 0; 181 182 de = vxfs_find_entry(dip, dp, &pp); 183 if (de) { 184 ino = de->d_ino; 185 kunmap(pp); 186 page_cache_release(pp); 187 } 188 189 return (ino); 190} 191 192/** 193 * vxfs_lookup - lookup pathname component 194 * @dip: dir in which we lookup 195 * @dp: dentry we lookup 196 * @nd: lookup nameidata 197 * 198 * Description: 199 * vxfs_lookup tries to lookup the pathname component described 200 * by @dp in @dip. 201 * 202 * Returns: 203 * A NULL-pointer on success, else an negative error code encoded 204 * in the return pointer. 205 */ 206static struct dentry * 207vxfs_lookup(struct inode *dip, struct dentry *dp, struct nameidata *nd) 208{ 209 struct inode *ip = NULL; 210 ino_t ino; 211 212 if (dp->d_name.len > VXFS_NAMELEN) 213 return ERR_PTR(-ENAMETOOLONG); 214 215 lock_kernel(); 216 ino = vxfs_inode_by_name(dip, dp); 217 if (ino) { 218 ip = vxfs_iget(dip->i_sb, ino); 219 if (IS_ERR(ip)) { 220 unlock_kernel(); 221 return ERR_CAST(ip); 222 } 223 } 224 unlock_kernel(); 225 d_add(dp, ip); 226 return NULL; 227} 228 229/** 230 * vxfs_readdir - read a directory 231 * @fp: the directory to read 232 * @retp: return buffer 233 * @filler: filldir callback 234 * 235 * Description: 236 * vxfs_readdir fills @retp with directory entries from @fp 237 * using the VFS supplied callback @filler. 238 * 239 * Returns: 240 * Zero. 241 */ 242static int 243vxfs_readdir(struct file *fp, void *retp, filldir_t filler) 244{ 245 struct inode *ip = fp->f_path.dentry->d_inode; 246 struct super_block *sbp = ip->i_sb; 247 u_long bsize = sbp->s_blocksize; 248 u_long page, npages, block, pblocks, nblocks, offset; 249 loff_t pos; 250 251 lock_kernel(); 252 253 switch ((long)fp->f_pos) { 254 case 0: 255 if (filler(retp, ".", 1, fp->f_pos, ip->i_ino, DT_DIR) < 0) 256 goto out; 257 fp->f_pos++; 258 /* fallthrough */ 259 case 1: 260 if (filler(retp, "..", 2, fp->f_pos, VXFS_INO(ip)->vii_dotdot, DT_DIR) < 0) 261 goto out; 262 fp->f_pos++; 263 /* fallthrough */ 264 } 265 266 pos = fp->f_pos - 2; 267 268 if (pos > VXFS_DIRROUND(ip->i_size)) { 269 unlock_kernel(); 270 return 0; 271 } 272 273 npages = dir_pages(ip); 274 nblocks = dir_blocks(ip); 275 pblocks = VXFS_BLOCK_PER_PAGE(sbp); 276 277 page = pos >> PAGE_CACHE_SHIFT; 278 offset = pos & ~PAGE_CACHE_MASK; 279 block = (u_long)(pos >> sbp->s_blocksize_bits) % pblocks; 280 281 for (; page < npages; page++, block = 0) { 282 caddr_t kaddr; 283 struct page *pp; 284 285 pp = vxfs_get_page(ip->i_mapping, page); 286 if (IS_ERR(pp)) 287 continue; 288 kaddr = (caddr_t)page_address(pp); 289 290 for (; block <= nblocks && block <= pblocks; block++) { 291 caddr_t baddr, limit; 292 struct vxfs_dirblk *dbp; 293 struct vxfs_direct *de; 294 295 baddr = kaddr + (block * bsize); 296 limit = baddr + bsize - VXFS_DIRLEN(1); 297 298 dbp = (struct vxfs_dirblk *)baddr; 299 de = (struct vxfs_direct *) 300 (offset ? 301 (kaddr + offset) : 302 (baddr + VXFS_DIRBLKOV(dbp))); 303 304 for (; (caddr_t)de <= limit; de = vxfs_next_entry(de)) { 305 int over; 306 307 if (!de->d_reclen) 308 break; 309 if (!de->d_ino) 310 continue; 311 312 offset = (caddr_t)de - kaddr; 313 over = filler(retp, de->d_name, de->d_namelen, 314 ((page << PAGE_CACHE_SHIFT) | offset) + 2, 315 de->d_ino, DT_UNKNOWN); 316 if (over) { 317 vxfs_put_page(pp); 318 goto done; 319 } 320 } 321 offset = 0; 322 } 323 vxfs_put_page(pp); 324 offset = 0; 325 } 326 327done: 328 fp->f_pos = ((page << PAGE_CACHE_SHIFT) | offset) + 2; 329out: 330 unlock_kernel(); 331 return 0; 332} 333