File: /usr/src/linux/fs/freevxfs/vxfs_lookup.c

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