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"
22#include "cifs_debug.h"
23
24/*
25 * CIFS filesystem definition for FS-Cache
26 */
27struct fscache_netfs cifs_fscache_netfs = {
28	.name = "cifs",
29	.version = 0,
30};
31
32/*
33 * Register CIFS for caching with FS-Cache
34 */
35int cifs_fscache_register(void)
36{
37	return fscache_register_netfs(&cifs_fscache_netfs);
38}
39
40/*
41 * Unregister CIFS for caching
42 */
43void cifs_fscache_unregister(void)
44{
45	fscache_unregister_netfs(&cifs_fscache_netfs);
46}
47
48/*
49 * Key layout of CIFS server cache index object
50 */
51struct cifs_server_key {
52	uint16_t	family;		/* address family */
53	uint16_t	port;		/* IP port */
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 */
63static 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;
67	const struct sockaddr *sa = (struct sockaddr *) &server->addr.sockAddr;
68	struct cifs_server_key *key = buffer;
69	uint16_t key_len = sizeof(struct cifs_server_key);
70
71	memset(key, 0, key_len);
72
73	/*
74	 * Should not be a problem as sin_family/sin6_family overlays
75	 * sa_family field
76	 */
77	switch (sa->sa_family) {
78	case AF_INET:
79		key->family = server->addr.sockAddr.sin_family;
80		key->port = server->addr.sockAddr.sin_port;
81		key->addr[0].ipv4_addr = server->addr.sockAddr.sin_addr;
82		key_len += sizeof(key->addr[0].ipv4_addr);
83		break;
84
85	case AF_INET6:
86		key->family = server->addr.sockAddr6.sin6_family;
87		key->port = server->addr.sockAddr6.sin6_port;
88		key->addr[0].ipv6_addr = server->addr.sockAddr6.sin6_addr;
89		key_len += sizeof(key->addr[0].ipv6_addr);
90		break;
91
92	default:
93		cERROR(1, "CIFS: Unknown network family '%d'", sa->sa_family);
94		key_len = 0;
95		break;
96	}
97
98	return key_len;
99}
100
101/*
102 * Server object for FS-Cache
103 */
104const struct fscache_cookie_def cifs_fscache_server_index_def = {
105	.name = "CIFS.server",
106	.type = FSCACHE_COOKIE_TYPE_INDEX,
107	.get_key = cifs_server_get_key,
108};
109
110/*
111 * Auxiliary data attached to CIFS superblock within the cache
112 */
113struct cifs_fscache_super_auxdata {
114	u64	resource_id;		/* unique server resource id */
115};
116
117static char *extract_sharename(const char *treename)
118{
119	const char *src;
120	char *delim, *dst;
121	int len;
122
123	/* skip double chars at the beginning */
124	src = treename + 2;
125
126	/* share name is always preceded by '\\' now */
127	delim = strchr(src, '\\');
128	if (!delim)
129		return ERR_PTR(-EINVAL);
130	delim++;
131	len = strlen(delim);
132
133	/* caller has to free the memory */
134	dst = kstrndup(delim, len, GFP_KERNEL);
135	if (!dst)
136		return ERR_PTR(-ENOMEM);
137
138	return dst;
139}
140
141/*
142 * Superblock object currently keyed by share name
143 */
144static uint16_t cifs_super_get_key(const void *cookie_netfs_data, void *buffer,
145				   uint16_t maxbuf)
146{
147	const struct cifsTconInfo *tcon = cookie_netfs_data;
148	char *sharename;
149	uint16_t len;
150
151	sharename = extract_sharename(tcon->treeName);
152	if (IS_ERR(sharename)) {
153		cFYI(1, "CIFS: couldn't extract sharename\n");
154		sharename = NULL;
155		return 0;
156	}
157
158	len = strlen(sharename);
159	if (len > maxbuf)
160		return 0;
161
162	memcpy(buffer, sharename, len);
163
164	kfree(sharename);
165
166	return len;
167}
168
169static uint16_t
170cifs_fscache_super_get_aux(const void *cookie_netfs_data, void *buffer,
171			   uint16_t maxbuf)
172{
173	struct cifs_fscache_super_auxdata auxdata;
174	const struct cifsTconInfo *tcon = cookie_netfs_data;
175
176	memset(&auxdata, 0, sizeof(auxdata));
177	auxdata.resource_id = tcon->resource_id;
178
179	if (maxbuf > sizeof(auxdata))
180		maxbuf = sizeof(auxdata);
181
182	memcpy(buffer, &auxdata, maxbuf);
183
184	return maxbuf;
185}
186
187static enum
188fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data,
189					      const void *data,
190					      uint16_t datalen)
191{
192	struct cifs_fscache_super_auxdata auxdata;
193	const struct cifsTconInfo *tcon = cookie_netfs_data;
194
195	if (datalen != sizeof(auxdata))
196		return FSCACHE_CHECKAUX_OBSOLETE;
197
198	memset(&auxdata, 0, sizeof(auxdata));
199	auxdata.resource_id = tcon->resource_id;
200
201	if (memcmp(data, &auxdata, datalen) != 0)
202		return FSCACHE_CHECKAUX_OBSOLETE;
203
204	return FSCACHE_CHECKAUX_OKAY;
205}
206
207/*
208 * Superblock object for FS-Cache
209 */
210const struct fscache_cookie_def cifs_fscache_super_index_def = {
211	.name = "CIFS.super",
212	.type = FSCACHE_COOKIE_TYPE_INDEX,
213	.get_key = cifs_super_get_key,
214	.get_aux = cifs_fscache_super_get_aux,
215	.check_aux = cifs_fscache_super_check_aux,
216};
217
218/*
219 * Auxiliary data attached to CIFS inode within the cache
220 */
221struct cifs_fscache_inode_auxdata {
222	struct timespec	last_write_time;
223	struct timespec	last_change_time;
224	u64		eof;
225};
226
227static uint16_t cifs_fscache_inode_get_key(const void *cookie_netfs_data,
228					   void *buffer, uint16_t maxbuf)
229{
230	const struct cifsInodeInfo *cifsi = cookie_netfs_data;
231	uint16_t keylen;
232
233	/* use the UniqueId as the key */
234	keylen = sizeof(cifsi->uniqueid);
235	if (keylen > maxbuf)
236		keylen = 0;
237	else
238		memcpy(buffer, &cifsi->uniqueid, keylen);
239
240	return keylen;
241}
242
243static void
244cifs_fscache_inode_get_attr(const void *cookie_netfs_data, uint64_t *size)
245{
246	const struct cifsInodeInfo *cifsi = cookie_netfs_data;
247
248	*size = cifsi->vfs_inode.i_size;
249}
250
251static uint16_t
252cifs_fscache_inode_get_aux(const void *cookie_netfs_data, void *buffer,
253			   uint16_t maxbuf)
254{
255	struct cifs_fscache_inode_auxdata auxdata;
256	const struct cifsInodeInfo *cifsi = cookie_netfs_data;
257
258	memset(&auxdata, 0, sizeof(auxdata));
259	auxdata.eof = cifsi->server_eof;
260	auxdata.last_write_time = cifsi->vfs_inode.i_mtime;
261	auxdata.last_change_time = cifsi->vfs_inode.i_ctime;
262
263	if (maxbuf > sizeof(auxdata))
264		maxbuf = sizeof(auxdata);
265
266	memcpy(buffer, &auxdata, maxbuf);
267
268	return maxbuf;
269}
270
271static enum
272fscache_checkaux cifs_fscache_inode_check_aux(void *cookie_netfs_data,
273					      const void *data,
274					      uint16_t datalen)
275{
276	struct cifs_fscache_inode_auxdata auxdata;
277	struct cifsInodeInfo *cifsi = cookie_netfs_data;
278
279	if (datalen != sizeof(auxdata))
280		return FSCACHE_CHECKAUX_OBSOLETE;
281
282	memset(&auxdata, 0, sizeof(auxdata));
283	auxdata.eof = cifsi->server_eof;
284	auxdata.last_write_time = cifsi->vfs_inode.i_mtime;
285	auxdata.last_change_time = cifsi->vfs_inode.i_ctime;
286
287	if (memcmp(data, &auxdata, datalen) != 0)
288		return FSCACHE_CHECKAUX_OBSOLETE;
289
290	return FSCACHE_CHECKAUX_OKAY;
291}
292
293static void cifs_fscache_inode_now_uncached(void *cookie_netfs_data)
294{
295	struct cifsInodeInfo *cifsi = cookie_netfs_data;
296	struct pagevec pvec;
297	pgoff_t first;
298	int loop, nr_pages;
299
300	pagevec_init(&pvec, 0);
301	first = 0;
302
303	cFYI(1, "cifs inode 0x%p now uncached", cifsi);
304
305	for (;;) {
306		nr_pages = pagevec_lookup(&pvec,
307					  cifsi->vfs_inode.i_mapping, first,
308					  PAGEVEC_SIZE - pagevec_count(&pvec));
309		if (!nr_pages)
310			break;
311
312		for (loop = 0; loop < nr_pages; loop++)
313			ClearPageFsCache(pvec.pages[loop]);
314
315		first = pvec.pages[nr_pages - 1]->index + 1;
316
317		pvec.nr = nr_pages;
318		pagevec_release(&pvec);
319		cond_resched();
320	}
321}
322
323const struct fscache_cookie_def cifs_fscache_inode_object_def = {
324	.name		= "CIFS.uniqueid",
325	.type		= FSCACHE_COOKIE_TYPE_DATAFILE,
326	.get_key	= cifs_fscache_inode_get_key,
327	.get_attr	= cifs_fscache_inode_get_attr,
328	.get_aux	= cifs_fscache_inode_get_aux,
329	.check_aux	= cifs_fscache_inode_check_aux,
330	.now_uncached	= cifs_fscache_inode_now_uncached,
331};
332