]>
Commit | Line | Data |
---|---|---|
f579cf3c SJ |
1 | /* |
2 | * fs/cifs/cache.c - CIFS filesystem cache index structure definitions | |
3 | * | |
4 | * Copyright (c) 2010 Novell, Inc. | |
5 | * Authors(s): Suresh Jayaraman (sjayaraman@suse.de> | |
6 | * | |
7 | * This library is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU Lesser General Public License as published | |
9 | * by the Free Software Foundation; either version 2.1 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This library is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See | |
15 | * the GNU Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public License | |
18 | * along with this library; if not, write to the Free Software | |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
20 | */ | |
21 | #include "fscache.h" | |
488f1d2d | 22 | #include "cifs_debug.h" |
f579cf3c SJ |
23 | |
24 | /* | |
25 | * CIFS filesystem definition for FS-Cache | |
26 | */ | |
27 | struct fscache_netfs cifs_fscache_netfs = { | |
28 | .name = "cifs", | |
29 | .version = 0, | |
30 | }; | |
31 | ||
32 | /* | |
33 | * Register CIFS for caching with FS-Cache | |
34 | */ | |
35 | int cifs_fscache_register(void) | |
36 | { | |
37 | return fscache_register_netfs(&cifs_fscache_netfs); | |
38 | } | |
39 | ||
40 | /* | |
41 | * Unregister CIFS for caching | |
42 | */ | |
43 | void cifs_fscache_unregister(void) | |
44 | { | |
45 | fscache_unregister_netfs(&cifs_fscache_netfs); | |
46 | } | |
47 | ||
488f1d2d SJ |
48 | /* |
49 | * Key layout of CIFS server cache index object | |
50 | */ | |
51 | struct cifs_server_key { | |
52 | uint16_t family; /* address family */ | |
5443d130 | 53 | __be16 port; /* IP port */ |
488f1d2d SJ |
54 | union { |
55 | struct in_addr ipv4_addr; | |
56 | struct in6_addr ipv6_addr; | |
57 | } addr[0]; | |
58 | }; | |
59 | ||
60 | /* | |
61 | * Server object keyed by {IPaddress,port,family} tuple | |
62 | */ | |
63 | static uint16_t cifs_server_get_key(const void *cookie_netfs_data, | |
64 | void *buffer, uint16_t maxbuf) | |
65 | { | |
66 | const struct TCP_Server_Info *server = cookie_netfs_data; | |
a9f1b85e PS |
67 | const struct sockaddr *sa = (struct sockaddr *) &server->dstaddr; |
68 | const struct sockaddr_in *addr = (struct sockaddr_in *) sa; | |
69 | const struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) sa; | |
488f1d2d SJ |
70 | struct cifs_server_key *key = buffer; |
71 | uint16_t key_len = sizeof(struct cifs_server_key); | |
72 | ||
73 | memset(key, 0, key_len); | |
74 | ||
75 | /* | |
76 | * Should not be a problem as sin_family/sin6_family overlays | |
77 | * sa_family field | |
78 | */ | |
79 | switch (sa->sa_family) { | |
80 | case AF_INET: | |
a9f1b85e PS |
81 | key->family = sa->sa_family; |
82 | key->port = addr->sin_port; | |
83 | key->addr[0].ipv4_addr = addr->sin_addr; | |
488f1d2d SJ |
84 | key_len += sizeof(key->addr[0].ipv4_addr); |
85 | break; | |
86 | ||
87 | case AF_INET6: | |
a9f1b85e PS |
88 | key->family = sa->sa_family; |
89 | key->port = addr6->sin6_port; | |
90 | key->addr[0].ipv6_addr = addr6->sin6_addr; | |
488f1d2d SJ |
91 | key_len += sizeof(key->addr[0].ipv6_addr); |
92 | break; | |
93 | ||
94 | default: | |
f96637be | 95 | cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family); |
488f1d2d SJ |
96 | key_len = 0; |
97 | break; | |
98 | } | |
99 | ||
100 | return key_len; | |
101 | } | |
102 | ||
103 | /* | |
104 | * Server object for FS-Cache | |
105 | */ | |
106 | const struct fscache_cookie_def cifs_fscache_server_index_def = { | |
107 | .name = "CIFS.server", | |
108 | .type = FSCACHE_COOKIE_TYPE_INDEX, | |
109 | .get_key = cifs_server_get_key, | |
110 | }; | |
d03382ce SJ |
111 | |
112 | /* | |
113 | * Auxiliary data attached to CIFS superblock within the cache | |
114 | */ | |
115 | struct cifs_fscache_super_auxdata { | |
116 | u64 resource_id; /* unique server resource id */ | |
117 | }; | |
118 | ||
119 | static char *extract_sharename(const char *treename) | |
120 | { | |
121 | const char *src; | |
122 | char *delim, *dst; | |
123 | int len; | |
124 | ||
125 | /* skip double chars at the beginning */ | |
126 | src = treename + 2; | |
127 | ||
128 | /* share name is always preceded by '\\' now */ | |
129 | delim = strchr(src, '\\'); | |
130 | if (!delim) | |
131 | return ERR_PTR(-EINVAL); | |
132 | delim++; | |
133 | len = strlen(delim); | |
134 | ||
135 | /* caller has to free the memory */ | |
136 | dst = kstrndup(delim, len, GFP_KERNEL); | |
137 | if (!dst) | |
138 | return ERR_PTR(-ENOMEM); | |
139 | ||
140 | return dst; | |
141 | } | |
142 | ||
143 | /* | |
144 | * Superblock object currently keyed by share name | |
145 | */ | |
146 | static uint16_t cifs_super_get_key(const void *cookie_netfs_data, void *buffer, | |
147 | uint16_t maxbuf) | |
148 | { | |
96daf2b0 | 149 | const struct cifs_tcon *tcon = cookie_netfs_data; |
d03382ce SJ |
150 | char *sharename; |
151 | uint16_t len; | |
152 | ||
153 | sharename = extract_sharename(tcon->treeName); | |
154 | if (IS_ERR(sharename)) { | |
f96637be | 155 | cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__); |
d03382ce SJ |
156 | sharename = NULL; |
157 | return 0; | |
158 | } | |
159 | ||
160 | len = strlen(sharename); | |
161 | if (len > maxbuf) | |
162 | return 0; | |
163 | ||
164 | memcpy(buffer, sharename, len); | |
165 | ||
166 | kfree(sharename); | |
167 | ||
168 | return len; | |
169 | } | |
170 | ||
171 | static uint16_t | |
172 | cifs_fscache_super_get_aux(const void *cookie_netfs_data, void *buffer, | |
173 | uint16_t maxbuf) | |
174 | { | |
175 | struct cifs_fscache_super_auxdata auxdata; | |
96daf2b0 | 176 | const struct cifs_tcon *tcon = cookie_netfs_data; |
d03382ce SJ |
177 | |
178 | memset(&auxdata, 0, sizeof(auxdata)); | |
179 | auxdata.resource_id = tcon->resource_id; | |
180 | ||
181 | if (maxbuf > sizeof(auxdata)) | |
182 | maxbuf = sizeof(auxdata); | |
183 | ||
184 | memcpy(buffer, &auxdata, maxbuf); | |
185 | ||
186 | return maxbuf; | |
187 | } | |
188 | ||
189 | static enum | |
190 | fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data, | |
191 | const void *data, | |
192 | uint16_t datalen) | |
193 | { | |
194 | struct cifs_fscache_super_auxdata auxdata; | |
96daf2b0 | 195 | const struct cifs_tcon *tcon = cookie_netfs_data; |
d03382ce SJ |
196 | |
197 | if (datalen != sizeof(auxdata)) | |
198 | return FSCACHE_CHECKAUX_OBSOLETE; | |
199 | ||
200 | memset(&auxdata, 0, sizeof(auxdata)); | |
201 | auxdata.resource_id = tcon->resource_id; | |
202 | ||
203 | if (memcmp(data, &auxdata, datalen) != 0) | |
204 | return FSCACHE_CHECKAUX_OBSOLETE; | |
205 | ||
206 | return FSCACHE_CHECKAUX_OKAY; | |
207 | } | |
208 | ||
209 | /* | |
210 | * Superblock object for FS-Cache | |
211 | */ | |
212 | const struct fscache_cookie_def cifs_fscache_super_index_def = { | |
213 | .name = "CIFS.super", | |
214 | .type = FSCACHE_COOKIE_TYPE_INDEX, | |
215 | .get_key = cifs_super_get_key, | |
216 | .get_aux = cifs_fscache_super_get_aux, | |
217 | .check_aux = cifs_fscache_super_check_aux, | |
218 | }; | |
219 | ||
9451a9a5 SJ |
220 | /* |
221 | * Auxiliary data attached to CIFS inode within the cache | |
222 | */ | |
223 | struct cifs_fscache_inode_auxdata { | |
224 | struct timespec last_write_time; | |
225 | struct timespec last_change_time; | |
226 | u64 eof; | |
227 | }; | |
228 | ||
229 | static uint16_t cifs_fscache_inode_get_key(const void *cookie_netfs_data, | |
230 | void *buffer, uint16_t maxbuf) | |
231 | { | |
232 | const struct cifsInodeInfo *cifsi = cookie_netfs_data; | |
233 | uint16_t keylen; | |
234 | ||
235 | /* use the UniqueId as the key */ | |
236 | keylen = sizeof(cifsi->uniqueid); | |
237 | if (keylen > maxbuf) | |
238 | keylen = 0; | |
239 | else | |
240 | memcpy(buffer, &cifsi->uniqueid, keylen); | |
241 | ||
242 | return keylen; | |
243 | } | |
244 | ||
245 | static void | |
246 | cifs_fscache_inode_get_attr(const void *cookie_netfs_data, uint64_t *size) | |
247 | { | |
248 | const struct cifsInodeInfo *cifsi = cookie_netfs_data; | |
249 | ||
250 | *size = cifsi->vfs_inode.i_size; | |
251 | } | |
252 | ||
253 | static uint16_t | |
254 | cifs_fscache_inode_get_aux(const void *cookie_netfs_data, void *buffer, | |
255 | uint16_t maxbuf) | |
256 | { | |
257 | struct cifs_fscache_inode_auxdata auxdata; | |
258 | const struct cifsInodeInfo *cifsi = cookie_netfs_data; | |
259 | ||
260 | memset(&auxdata, 0, sizeof(auxdata)); | |
261 | auxdata.eof = cifsi->server_eof; | |
262 | auxdata.last_write_time = cifsi->vfs_inode.i_mtime; | |
263 | auxdata.last_change_time = cifsi->vfs_inode.i_ctime; | |
264 | ||
265 | if (maxbuf > sizeof(auxdata)) | |
266 | maxbuf = sizeof(auxdata); | |
267 | ||
268 | memcpy(buffer, &auxdata, maxbuf); | |
269 | ||
270 | return maxbuf; | |
271 | } | |
272 | ||
273 | static enum | |
274 | fscache_checkaux cifs_fscache_inode_check_aux(void *cookie_netfs_data, | |
275 | const void *data, | |
276 | uint16_t datalen) | |
277 | { | |
278 | struct cifs_fscache_inode_auxdata auxdata; | |
279 | struct cifsInodeInfo *cifsi = cookie_netfs_data; | |
280 | ||
281 | if (datalen != sizeof(auxdata)) | |
282 | return FSCACHE_CHECKAUX_OBSOLETE; | |
283 | ||
284 | memset(&auxdata, 0, sizeof(auxdata)); | |
285 | auxdata.eof = cifsi->server_eof; | |
286 | auxdata.last_write_time = cifsi->vfs_inode.i_mtime; | |
287 | auxdata.last_change_time = cifsi->vfs_inode.i_ctime; | |
288 | ||
289 | if (memcmp(data, &auxdata, datalen) != 0) | |
290 | return FSCACHE_CHECKAUX_OBSOLETE; | |
291 | ||
292 | return FSCACHE_CHECKAUX_OKAY; | |
293 | } | |
294 | ||
85f2d6b4 SJ |
295 | static void cifs_fscache_inode_now_uncached(void *cookie_netfs_data) |
296 | { | |
297 | struct cifsInodeInfo *cifsi = cookie_netfs_data; | |
298 | struct pagevec pvec; | |
299 | pgoff_t first; | |
300 | int loop, nr_pages; | |
301 | ||
302 | pagevec_init(&pvec, 0); | |
303 | first = 0; | |
304 | ||
f96637be | 305 | cifs_dbg(FYI, "%s: cifs inode 0x%p now uncached\n", __func__, cifsi); |
85f2d6b4 SJ |
306 | |
307 | for (;;) { | |
308 | nr_pages = pagevec_lookup(&pvec, | |
309 | cifsi->vfs_inode.i_mapping, first, | |
310 | PAGEVEC_SIZE - pagevec_count(&pvec)); | |
311 | if (!nr_pages) | |
312 | break; | |
313 | ||
314 | for (loop = 0; loop < nr_pages; loop++) | |
315 | ClearPageFsCache(pvec.pages[loop]); | |
316 | ||
317 | first = pvec.pages[nr_pages - 1]->index + 1; | |
318 | ||
319 | pvec.nr = nr_pages; | |
320 | pagevec_release(&pvec); | |
321 | cond_resched(); | |
322 | } | |
323 | } | |
324 | ||
9451a9a5 SJ |
325 | const struct fscache_cookie_def cifs_fscache_inode_object_def = { |
326 | .name = "CIFS.uniqueid", | |
327 | .type = FSCACHE_COOKIE_TYPE_DATAFILE, | |
328 | .get_key = cifs_fscache_inode_get_key, | |
329 | .get_attr = cifs_fscache_inode_get_attr, | |
330 | .get_aux = cifs_fscache_inode_get_aux, | |
331 | .check_aux = cifs_fscache_inode_check_aux, | |
85f2d6b4 | 332 | .now_uncached = cifs_fscache_inode_now_uncached, |
9451a9a5 | 333 | }; |