File: /usr/src/linux/fs/smbfs/cache.c

1     /*
2      *  cache.c
3      *
4      * Copyright (C) 1997 by Bill Hawes
5      *
6      * Routines to support directory cacheing using the page cache.
7      * This cache code is almost directly taken from ncpfs.
8      *
9      * Please add a note about your changes to smbfs in the ChangeLog file.
10      */
11     
12     #include <linux/sched.h>
13     #include <linux/errno.h>
14     #include <linux/kernel.h>
15     #include <linux/mm.h>
16     #include <linux/dirent.h>
17     #include <linux/smb_fs.h>
18     #include <linux/pagemap.h>
19     
20     #include <asm/page.h>
21     
22     #include "smb_debug.h"
23     
24     /*
25      * Force the next attempt to use the cache to be a timeout.
26      * If we can't find the page that's fine, it will cause a refresh.
27      */
28     void
29     smb_invalid_dir_cache(struct inode * dir)
30     {
31     	struct smb_sb_info *server = server_from_inode(dir);
32     	union  smb_dir_cache *cache = NULL;
33     	struct page *page = NULL;
34     
35     	page = grab_cache_page(&dir->i_data, 0);
36     	if (!page)
37     		goto out;
38     
39     	if (!Page_Uptodate(page))
40     		goto out_unlock;
41     
42     	cache = kmap(page);
43     	cache->head.time = jiffies - SMB_MAX_AGE(server);
44     
45     	kunmap(page);
46     	SetPageUptodate(page);
47     out_unlock:
48     	UnlockPage(page);
49     	page_cache_release(page);
50     out:
51     	return;
52     }
53     
54     /*
55      * Mark all dentries for 'parent' as invalid, forcing them to be re-read
56      */
57     void
58     smb_invalidate_dircache_entries(struct dentry *parent)
59     {
60     	struct smb_sb_info *server = server_from_dentry(parent);
61     	struct list_head *next;
62     	struct dentry *dentry;
63     
64     	spin_lock(&dcache_lock);
65     	next = parent->d_subdirs.next;
66     	while (next != &parent->d_subdirs) {
67     		dentry = list_entry(next, struct dentry, d_child);
68     		dentry->d_fsdata = NULL;
69     		smb_age_dentry(server, dentry);
70     		next = next->next;
71     	}
72     	spin_unlock(&dcache_lock);
73     }
74     
75     /*
76      * dget, but require that fpos and parent matches what the dentry contains.
77      * dentry is not known to be a valid pointer at entry.
78      */
79     struct dentry *
80     smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
81     {
82     	struct dentry *dent = dentry;
83     	struct list_head *next;
84     
85     	if (d_validate(dent, parent)) {
86     		if (dent->d_name.len <= SMB_MAXPATHLEN &&
87     		    (unsigned long)dent->d_fsdata == fpos) {
88     			if (!dent->d_inode) {
89     				dput(dent);
90     				dent = NULL;
91     			}
92     			return dent;
93     		}
94     		dput(dent);
95     	}
96     
97     	/* If a pointer is invalid, we search the dentry. */
98     	spin_lock(&dcache_lock);
99     	next = parent->d_subdirs.next;
100     	while (next != &parent->d_subdirs) {
101     		dent = list_entry(next, struct dentry, d_child);
102     		if ((unsigned long)dent->d_fsdata == fpos) {
103     			if (dent->d_inode)
104     				dget_locked(dent);
105     			else
106     				dent = NULL;
107     			goto out_unlock;
108     		}
109     		next = next->next;
110     	}
111     	dent = NULL;
112     out_unlock:
113     	spin_unlock(&dcache_lock);
114     	return dent;
115     }
116     
117     
118     /*
119      * Create dentry/inode for this file and add it to the dircache.
120      */
121     int
122     smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
123     	       struct smb_cache_control *ctrl, struct qstr *qname,
124     	       struct smb_fattr *entry)
125     {
126     	struct dentry *newdent, *dentry = filp->f_dentry;
127     	struct inode *newino, *inode = dentry->d_inode;
128     	struct smb_cache_control ctl = *ctrl;
129     	int valid = 0;
130     	int hashed = 0;
131     	ino_t ino = 0;
132     
133     	qname->hash = full_name_hash(qname->name, qname->len);
134     
135     	if (dentry->d_op && dentry->d_op->d_hash)
136     		if (dentry->d_op->d_hash(dentry, qname) != 0)
137     			goto end_advance;
138     
139     	newdent = d_lookup(dentry, qname);
140     
141     	if (!newdent) {
142     		newdent = d_alloc(dentry, qname);
143     		if (!newdent)
144     			goto end_advance;
145     	} else {
146     		hashed = 1;
147     		memcpy((char *) newdent->d_name.name, qname->name,
148     		       newdent->d_name.len);
149     	}
150     
151     	if (!newdent->d_inode) {
152     		smb_renew_times(newdent);
153     		entry->f_ino = iunique(inode->i_sb, 2);
154     		newino = smb_iget(inode->i_sb, entry);
155     		if (newino) {
156     			smb_new_dentry(newdent);
157     			d_instantiate(newdent, newino);
158     			if (!hashed)
159     				d_rehash(newdent);
160     		}
161     	} else
162     		smb_set_inode_attr(newdent->d_inode, entry);
163     
164             if (newdent->d_inode) {
165     		ino = newdent->d_inode->i_ino;
166     		newdent->d_fsdata = (void *) ctl.fpos;
167     		smb_new_dentry(newdent);
168     	}
169     
170     	if (ctl.idx >= SMB_DIRCACHE_SIZE) {
171     		if (ctl.page) {
172     			kunmap(ctl.page);
173     			SetPageUptodate(ctl.page);
174     			UnlockPage(ctl.page);
175     			page_cache_release(ctl.page);
176     		}
177     		ctl.cache = NULL;
178     		ctl.idx  -= SMB_DIRCACHE_SIZE;
179     		ctl.ofs  += 1;
180     		ctl.page  = grab_cache_page(&inode->i_data, ctl.ofs);
181     		if (ctl.page)
182     			ctl.cache = kmap(ctl.page);
183     	}
184     	if (ctl.cache) {
185     		ctl.cache->dentry[ctl.idx] = newdent;
186     		valid = 1;
187     	}
188     	dput(newdent);
189     
190     end_advance:
191     	if (!valid)
192     		ctl.valid = 0;
193     	if (!ctl.filled && (ctl.fpos == filp->f_pos)) {
194     		if (!ino)
195     			ino = find_inode_number(dentry, qname);
196     		if (!ino)
197     			ino = iunique(inode->i_sb, 2);
198     		ctl.filled = filldir(dirent, qname->name, qname->len,
199     				     filp->f_pos, ino, DT_UNKNOWN);
200     		if (!ctl.filled)
201     			filp->f_pos += 1;
202     	}
203     	ctl.fpos += 1;
204     	ctl.idx  += 1;
205     	*ctrl = ctl;
206     	return (ctl.valid || !ctl.filled);
207     }
208