• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/samba-3.5.8/source3/modules/
1/*
2  Unix SMB/CIFS implementation.
3  Samba VFS module for handling offline files
4  with Tivoli Storage Manager Space Management
5
6  (c) Alexander Bokovoy, 2007, 2008
7  (c) Andrew Tridgell, 2007, 2008
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 3 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19   You should have received a copy of the GNU General Public License
20   along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22/*
23  This VFS module accepts following options:
24  tsmsm: hsm script = <path to hsm script> (default does nothing)
25         hsm script should point to a shell script which accepts two arguments:
26	 <operation> <filepath>
27	 where <operation> is currently 'offline' to set offline status of the <filepath>
28
29  tsmsm: online ratio = ratio to check reported size against actual file size (0.5 by default)
30  tsmsm: attribute name = name of DMAPI attribute that is present when a file is offline.
31  Default is "IBMobj" (which is what GPFS uses)
32
33  The TSMSM VFS module tries to avoid calling expensive DMAPI calls with some heuristics
34  based on the fact that number of blocks reported of a file multiplied by 512 will be
35  bigger than 'online ratio' of actual size for online (non-migrated) files.
36
37  If checks fail, we call DMAPI and ask for specific attribute which present for
38  offline (migrated) files. If this attribute presents, we consider file offline.
39 */
40
41#include "includes.h"
42
43#ifndef USE_DMAPI
44#error "This module requires DMAPI support!"
45#endif
46
47#ifdef HAVE_XFS_DMAPI_H
48#include <xfs/dmapi.h>
49#elif defined(HAVE_SYS_DMI_H)
50#include <sys/dmi.h>
51#elif defined(HAVE_SYS_JFSDMAPI_H)
52#include <sys/jfsdmapi.h>
53#elif defined(HAVE_SYS_DMAPI_H)
54#include <sys/dmapi.h>
55#elif defined(HAVE_DMAPI_H)
56#include <dmapi.h>
57#endif
58
59#ifndef _ISOC99_SOURCE
60#define _ISOC99_SOURCE
61#endif
62
63#include <math.h>
64
65/* optimisation tunables - used to avoid the DMAPI slow path */
66#define FILE_IS_ONLINE_RATIO      0.5
67
68/* default attribute name to look for */
69#define DM_ATTRIB_OBJECT "IBMObj"
70
71struct tsmsm_struct {
72	float online_ratio;
73	char *hsmscript;
74	const char *attrib_name;
75	const char *attrib_value;
76};
77
78static void tsmsm_free_data(void **pptr) {
79	struct tsmsm_struct **tsmd = (struct tsmsm_struct **)pptr;
80	if(!tsmd) return;
81	TALLOC_FREE(*tsmd);
82}
83
84/*
85   called when a client connects to a share
86*/
87static int tsmsm_connect(struct vfs_handle_struct *handle,
88			 const char *service,
89			 const char *user) {
90	struct tsmsm_struct *tsmd;
91	const char *fres;
92	const char *tsmname;
93        int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
94
95	if (ret < 0) {
96		return ret;
97	}
98
99	tsmd = TALLOC_ZERO_P(handle, struct tsmsm_struct);
100	if (!tsmd) {
101		SMB_VFS_NEXT_DISCONNECT(handle);
102		DEBUG(0,("tsmsm_connect: out of memory!\n"));
103		return -1;
104	}
105
106	if (!dmapi_have_session()) {
107		SMB_VFS_NEXT_DISCONNECT(handle);
108		DEBUG(0,("tsmsm_connect: no DMAPI session for Samba is available!\n"));
109		TALLOC_FREE(tsmd);
110		return -1;
111	}
112
113	tsmname = (handle->param ? handle->param : "tsmsm");
114
115	/* Get 'hsm script' and 'dmapi attribute' parameters to tsmd context */
116	tsmd->hsmscript = lp_parm_talloc_string(SNUM(handle->conn), tsmname,
117						"hsm script", NULL);
118	talloc_steal(tsmd, tsmd->hsmscript);
119
120	tsmd->attrib_name = lp_parm_talloc_string(SNUM(handle->conn), tsmname,
121						  "dmapi attribute", DM_ATTRIB_OBJECT);
122	talloc_steal(tsmd, tsmd->attrib_name);
123
124	tsmd->attrib_value = lp_parm_talloc_string(SNUM(handle->conn), "tsmsm",
125						   "dmapi value", NULL);
126	talloc_steal(tsmd, tsmd->attrib_value);
127
128	/* retrieve 'online ratio'. In case of error default to FILE_IS_ONLINE_RATIO */
129	fres = lp_parm_const_string(SNUM(handle->conn), tsmname,
130				    "online ratio", NULL);
131	if (fres == NULL) {
132		tsmd->online_ratio = FILE_IS_ONLINE_RATIO;
133	} else {
134		tsmd->online_ratio = strtof(fres, NULL);
135		if (tsmd->online_ratio > 1.0 ||
136		    tsmd->online_ratio <= 0.0) {
137			DEBUG(1, ("tsmsm_connect: invalid online ration %f - using %f.\n",
138				  tsmd->online_ratio, (float)FILE_IS_ONLINE_RATIO));
139		}
140	}
141
142        /* Store the private data. */
143        SMB_VFS_HANDLE_SET_DATA(handle, tsmd, tsmsm_free_data,
144                                struct tsmsm_struct, return -1);
145        return 0;
146}
147
148static bool tsmsm_is_offline(struct vfs_handle_struct *handle,
149			    const char *path,
150			    SMB_STRUCT_STAT *stbuf) {
151	struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
152	const dm_sessid_t *dmsession_id;
153	void *dmhandle = NULL;
154	size_t dmhandle_len = 0;
155	size_t rlen;
156	dm_attrname_t dmname;
157	int ret, lerrno;
158	bool offline;
159	char *buf = NULL;
160	size_t buflen;
161
162        /* if the file has more than FILE_IS_ONLINE_RATIO of blocks available,
163	   then assume it is not offline (it may not be 100%, as it could be sparse) */
164	if (512 * (off_t)stbuf->st_ex_blocks >=
165	    stbuf->st_ex_size * tsmd->online_ratio) {
166		DEBUG(10,("%s not offline: st_blocks=%ld st_size=%ld "
167			  "online_ratio=%.2f\n", path,
168			  (long)stbuf->st_ex_blocks,
169			  (long)stbuf->st_ex_size, tsmd->online_ratio));
170		return false;
171	}
172
173	dmsession_id = dmapi_get_current_session();
174	if (dmsession_id == NULL) {
175		DEBUG(2, ("tsmsm_is_offline: no DMAPI session available? "
176			  "Assume file is online.\n"));
177		return false;
178	}
179
180        /* using POSIX capabilities does not work here. It's a slow path, so
181	 * become_root() is just as good anyway (tridge)
182	 */
183
184	/* Also, AIX has DMAPI but no POSIX capablities support. In this case,
185	 * we need to be root to do DMAPI manipulations.
186	 */
187	become_root();
188
189	/* go the slow DMAPI route */
190	if (dm_path_to_handle((char*)path, &dmhandle, &dmhandle_len) != 0) {
191		DEBUG(2,("dm_path_to_handle failed - assuming offline (%s) - %s\n",
192			 path, strerror(errno)));
193		offline = true;
194		goto done;
195	}
196
197	memset(&dmname, 0, sizeof(dmname));
198	strlcpy((char *)&dmname.an_chars[0], tsmd->attrib_name, sizeof(dmname.an_chars));
199
200	if (tsmd->attrib_value != NULL) {
201		buflen = strlen(tsmd->attrib_value);
202	} else {
203		buflen = 1;
204	}
205	buf = talloc_zero_size(tsmd, buflen);
206	if (buf == NULL) {
207		DEBUG(0,("out of memory in tsmsm_is_offline -- assuming online (%s)\n", path));
208		errno = ENOMEM;
209		offline = false;
210		goto done;
211	}
212
213	do {
214		lerrno = 0;
215
216		ret = dm_get_dmattr(*dmsession_id, dmhandle, dmhandle_len,
217				    DM_NO_TOKEN, &dmname, buflen, buf, &rlen);
218		if (ret == -1 && errno == EINVAL) {
219			DEBUG(0, ("Stale DMAPI session, re-creating it.\n"));
220			lerrno = EINVAL;
221			if (dmapi_new_session()) {
222				dmsession_id = dmapi_get_current_session();
223			} else {
224				DEBUG(0,
225				      ("Unable to re-create DMAPI session, assuming offline (%s) - %s\n",
226				       path, strerror(errno)));
227				offline = true;
228				dm_handle_free(dmhandle, dmhandle_len);
229				goto done;
230			}
231		}
232	} while (ret == -1 && lerrno == EINVAL);
233
234	/* check if we need a specific attribute value */
235	if (tsmd->attrib_value != NULL) {
236		offline = (ret == 0 && rlen == buflen &&
237			    memcmp(buf, tsmd->attrib_value, buflen) == 0);
238	} else {
239		/* its offline if the specified DMAPI attribute exists */
240		offline = (ret == 0 || (ret == -1 && errno == E2BIG));
241	}
242
243	DEBUG(10,("dm_get_dmattr %s ret=%d (%s)\n", path, ret, strerror(errno)));
244
245	ret = 0;
246
247	dm_handle_free(dmhandle, dmhandle_len);
248
249done:
250	talloc_free(buf);
251	unbecome_root();
252	return offline;
253}
254
255
256static bool tsmsm_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
257{
258	SMB_STRUCT_STAT sbuf;
259	struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
260	/* see if the file might be offline. This is called before each IO
261	   to ensure we use AIO if the file is offline. We don't do the full dmapi
262	   call as that would be too slow, instead we err on the side of using AIO
263	   if the file might be offline
264	*/
265	if(SMB_VFS_FSTAT(fsp, &sbuf) == 0) {
266		DEBUG(10,("tsmsm_aio_force st_blocks=%ld st_size=%ld "
267			  "online_ratio=%.2f\n", (long)sbuf.st_ex_blocks,
268			  (long)sbuf.st_ex_size, tsmd->online_ratio));
269		return !(512 * (off_t)sbuf.st_ex_blocks >=
270			 sbuf.st_ex_size * tsmd->online_ratio);
271	}
272	return false;
273}
274
275static ssize_t tsmsm_aio_return(struct vfs_handle_struct *handle, struct files_struct *fsp,
276				SMB_STRUCT_AIOCB *aiocb)
277{
278	ssize_t result;
279
280	result = SMB_VFS_NEXT_AIO_RETURN(handle, fsp, aiocb);
281	if(result >= 0) {
282		notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
283			     FILE_NOTIFY_CHANGE_ATTRIBUTES,
284			     fsp->fsp_name->base_name);
285	}
286
287	return result;
288}
289
290static ssize_t tsmsm_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, const DATA_BLOB *hdr,
291			      SMB_OFF_T offset, size_t n)
292{
293	bool file_offline = tsmsm_aio_force(handle, fsp);
294
295	if (file_offline) {
296		DEBUG(10,("tsmsm_sendfile on offline file - rejecting\n"));
297		errno = ENOSYS;
298		return -1;
299	}
300
301	return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
302}
303
304/* We do overload pread to allow notification when file becomes online after offline status */
305/* We don't intercept SMB_VFS_READ here because all file I/O now goes through SMB_VFS_PREAD instead */
306static ssize_t tsmsm_pread(struct vfs_handle_struct *handle, struct files_struct *fsp,
307			   void *data, size_t n, SMB_OFF_T offset) {
308	ssize_t result;
309	bool notify_online = tsmsm_aio_force(handle, fsp);
310
311	result = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
312	if((result != -1) && notify_online) {
313	    /* We can't actually force AIO at this point (came here not from reply_read_and_X)
314	       what we can do is to send notification that file became online
315	    */
316		notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
317			     FILE_NOTIFY_CHANGE_ATTRIBUTES,
318			     fsp->fsp_name->base_name);
319	}
320
321	return result;
322}
323
324static ssize_t tsmsm_pwrite(struct vfs_handle_struct *handle, struct files_struct *fsp,
325			    const void *data, size_t n, SMB_OFF_T offset) {
326	ssize_t result;
327	bool notify_online = tsmsm_aio_force(handle, fsp);
328
329	result = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
330	if((result != -1) && notify_online) {
331	    /* We can't actually force AIO at this point (came here not from reply_read_and_X)
332	       what we can do is to send notification that file became online
333	    */
334		notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
335			     FILE_NOTIFY_CHANGE_ATTRIBUTES,
336			     fsp->fsp_name->base_name);
337	}
338
339	return result;
340}
341
342static int tsmsm_set_offline(struct vfs_handle_struct *handle,
343			     const char *path) {
344	struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
345	int result = 0;
346	char *command;
347
348	if (tsmd->hsmscript == NULL) {
349		/* no script enabled */
350		DEBUG(1, ("tsmsm_set_offline: No 'tsmsm:hsm script' configured\n"));
351		return 0;
352	}
353
354	/* Now, call the script */
355	command = talloc_asprintf(tsmd, "%s offline \"%s\"", tsmd->hsmscript, path);
356	if(!command) {
357		DEBUG(1, ("tsmsm_set_offline: can't allocate memory to run hsm script"));
358		return -1;
359	}
360	DEBUG(10, ("tsmsm_set_offline: Running [%s]\n", command));
361	if((result = smbrun(command, NULL)) != 0) {
362		DEBUG(1,("tsmsm_set_offline: Running [%s] returned %d\n", command, result));
363	}
364	TALLOC_FREE(command);
365	return result;
366}
367
368static uint32_t tsmsm_fs_capabilities(struct vfs_handle_struct *handle,
369			enum timestamp_set_resolution *p_ts_res)
370{
371	return SMB_VFS_NEXT_FS_CAPABILITIES(handle, p_ts_res) | FILE_SUPPORTS_REMOTE_STORAGE | FILE_SUPPORTS_REPARSE_POINTS;
372}
373
374static struct vfs_fn_pointers tsmsm_fns = {
375	.connect_fn = tsmsm_connect,
376	.fs_capabilities = tsmsm_fs_capabilities,
377	.aio_force = tsmsm_aio_force,
378	.aio_return_fn = tsmsm_aio_return,
379	.pread = tsmsm_pread,
380	.pwrite = tsmsm_pwrite,
381	.sendfile = tsmsm_sendfile,
382	.is_offline = tsmsm_is_offline,
383	.set_offline = tsmsm_set_offline,
384};
385
386NTSTATUS vfs_tsmsm_init(void);
387NTSTATUS vfs_tsmsm_init(void)
388{
389	return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
390				"tsmsm", &tsmsm_fns);
391}
392