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