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