1    	/*
2    	 * vim:noexpandtab:shiftwidth=8:tabstop=8:
3    	 *
4    	 * Copyright (C) Panasas Inc., 2011
5    	 * Author: Jim Lieb jlieb@panasas.com
6    	 *
7    	 * contributeur : Philippe DENIEL   philippe.deniel@cea.fr
8    	 *                Thomas LEIBOVICI  thomas.leibovici@cea.fr
9    	 *
10   	 *
11   	 * This program is free software; you can redistribute it and/or
12   	 * modify it under the terms of the GNU Lesser General Public
13   	 * License as published by the Free Software Foundation; either
14   	 * version 3 of the License, or (at your option) any later version.
15   	 *
16   	 * This program is distributed in the hope that it will be useful,
17   	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19   	 * Lesser General Public License for more details.
20   	 *
21   	 * You should have received a copy of the GNU Lesser General Public
22   	 * License along with this library; if not, write to the Free Software
23   	 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
24   	 *
25   	 * -------------
26   	 */
27   	
28   	#ifdef HAVE_CONFIG_H
29   	#include "config.h"
30   	#endif
31   	
32   	#include "fsal.h"
33   	#include "fsal_handle_syscalls.h"
34   	#include <sys/types.h>
35   	#include <sys/syscall.h>
36   	#include <xfs/xfs.h>
37   	#include <xfs/handle.h>
38   	#include "gsh_list.h"
39   	#include "fsal_convert.h"
40   	#include "FSAL/fsal_commonlib.h"
41   	#include "../vfs_methods.h"
42   	#include "handle_syscalls.h"
43   	
44   	void display_xfs_handle(struct display_buffer *dspbuf,
45   				struct vfs_file_handle *fh)
46   	{
47   		xfs_handle_t *hdl = (xfs_handle_t *) fh->handle_data;
48   	
49   		(void) display_printf(dspbuf,
50   				      "Handle len %hhu: fsid=0x%016"
51   				      PRIx32".0x%016"PRIx32
52   				      " fid_len=%"PRIu16
53   				      " fid_pad=%"PRIu16
54   				      " fid_gen=%"PRIu32
55   				      " fid_ino=%"PRIu64,
56   				      fh->handle_len,
57   				      hdl->ha_fsid.val[0],
58   				      hdl->ha_fsid.val[1],
59   				      hdl->ha_fid.fid_len,
60   				      hdl->ha_fid.fid_pad,
61   				      hdl->ha_fid.fid_gen,
(1) Event invalid_type: Argument "hdl->ha_fid.fid_ino" to format specifier "%lu" was expected to have type "unsigned long" but has type "unsigned long long". [details]
62   				      hdl->ha_fid.fid_ino);
63   	}
64   	
65   	#define LogXFSHandle(fh)						\
66   		do {								\
67   			if (isMidDebug(COMPONENT_FSAL)) {			\
68   				char buf[256] = "\0";				\
69   				struct display_buffer dspbuf =			\
70   						{sizeof(buf), buf, buf};	\
71   										\
72   				display_xfs_handle(&dspbuf, fh);		\
73   										\
74   				LogMidDebug(COMPONENT_FSAL, "%s", buf);		\
75   			}							\
76   		} while (0)
77   	
78   	static int xfs_fsal_bulkstat_inode(int fd, xfs_ino_t ino, xfs_bstat_t *bstat)
79   	{
80   		xfs_fsop_bulkreq_t bulkreq;
81   		__u64 i = ino;
82   	
83   		bulkreq.lastip = &i;
84   		bulkreq.icount = 1;
85   		bulkreq.ubuffer = bstat;
86   		bulkreq.ocount = NULL;
87   		return ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq);
88   	}
89   	
90   	static int xfs_fsal_inode2handle(int fd, ino_t ino, vfs_file_handle_t *fh)
91   	{
92   		xfs_bstat_t bstat;
93   		xfs_handle_t *hdl = (xfs_handle_t *) fh->handle_data;
94   		void *data;
95   		size_t sz;
96   	
97   		if (fh->handle_len < sizeof(*hdl)) {
98   			errno = E2BIG;
99   			return -1;
100  		}
101  	
102  		/* Get the information pertinent to this inode, and
103  		 * the file handle for the reference fd.
104  		 */
105  		if ((xfs_fsal_bulkstat_inode(fd, ino, &bstat) < 0) ||
106  		    (fd_to_handle(fd, &data, &sz) < 0))
107  			return -1;
108  	
109  		/* Copy the fsid from the reference fd */
110  		memcpy(&hdl->ha_fsid, data, sizeof(xfs_fsid_t));
111  	
112  		/* Fill in the rest of the handle with the information
113  		 * pertinent to this inode.
114  		 */
115  		hdl->ha_fid.fid_len = sizeof(xfs_handle_t) -
116  				      sizeof(xfs_fsid_t) -
117  				      sizeof(hdl->ha_fid.fid_len);
118  		hdl->ha_fid.fid_pad = 0;
119  		hdl->ha_fid.fid_gen = bstat.bs_gen;
120  		hdl->ha_fid.fid_ino = bstat.bs_ino;
121  	
122  		fh->handle_len = sizeof(*hdl);
123  	
124  		free_handle(data, sz);
125  		return 0;
126  	}
127  	
128  	int vfs_open_by_handle(struct vfs_filesystem *fs,
129  			       vfs_file_handle_t *fh, int openflags,
130  			       fsal_errors_t *fsal_error)
131  	{
132  		int fd;
133  	
134  		LogXFSHandle(fh);
135  	
136  		if (openflags == (O_PATH | O_NOACCESS))
137  			openflags = O_DIRECTORY;
138  	
139  		fd = open_by_handle(fh->handle_data, fh->handle_len, openflags);
140  		if (fd < 0) {
141  			fd = -errno;
142  			if (fd == -ENOENT)
143  				*fsal_error = posix2fsal_error(ESTALE);
144  			else
145  				*fsal_error = posix2fsal_error(-fd);
146  		}
147  		return fd;
148  	}
149  	
150  	int vfs_fd_to_handle(int fd, struct fsal_filesystem *fs,
151  			     vfs_file_handle_t *fh)
152  	{
153  		void *data;
154  		size_t sz;
155  		int rv = 0;
156  	
157  		if (fd_to_handle(fd, &data, &sz) < 0)
158  			return -1;
159  	
160  		if (sz >= fh->handle_len) {
161  			errno = E2BIG;
162  			rv = -1;
163  		} else {
164  			memcpy(fh->handle_data, data, sz);
165  			fh->handle_len = sz;
166  	
167  			LogXFSHandle(fh);
168  		}
169  		free_handle(data, sz);
170  		return rv;
171  	}
172  	
173  	int vfs_name_to_handle(int fd,
174  			       struct fsal_filesystem *fs,
175  			       const char *name,
176  			       vfs_file_handle_t *fh)
177  	{
178  		int retval;
179  		struct stat stat;
180  	
181  		if (fstatat(fd, name, &stat, AT_SYMLINK_NOFOLLOW) < 0)
182  			return -1;
183  	
184  		if (S_ISDIR(stat.st_mode) || S_ISREG(stat.st_mode)) {
185  			int e;
186  			int tmpfd = openat(fd, name, O_RDONLY | O_NOFOLLOW, 0600);
187  	
188  			if (tmpfd < 0)
189  				return -1;
190  	
191  			retval = vfs_fd_to_handle(tmpfd, fs, fh);
192  			e = errno;
193  			close(tmpfd);
194  			errno = e;
195  		} else {
196  			retval = xfs_fsal_inode2handle(fd, stat.st_ino, fh);
197  		}
198  		LogXFSHandle(fh);
199  		return retval;
200  	}
201  	
202  	int vfs_readlink(struct vfs_fsal_obj_handle *hdl,
203  			 fsal_errors_t *ferr)
204  	{
205  		char ldata[MAXPATHLEN + 1];
206  		int retval;
207  	
208  		LogXFSHandle(hdl->handle);
209  		retval = readlink_by_handle(hdl->handle->handle_data,
210  					    hdl->handle->handle_len,
211  					    ldata, sizeof(ldata));
212  		if (retval < 0) {
213  			retval = -errno;
214  			*ferr = posix2fsal_error(retval);
215  			goto out;
216  		}
217  	
218  		ldata[retval] = '\0';
219  	
220  		hdl->u.symlink.link_content = gsh_strdup(ldata);
221  	
222  		hdl->u.symlink.link_size = retval + 1;
223  		retval = 0;
224  	
225  	 out:
226  		return retval;
227  	}
228  	
229  	int vfs_extract_fsid(vfs_file_handle_t *fh,
230  			     enum fsid_type *fsid_type,
231  			     struct fsal_fsid__ *fsid)
232  	{
233  		xfs_handle_t *hdl = (xfs_handle_t *) fh->handle_data;
234  	
235  		LogXFSHandle(fh);
236  	
237  		if (hdl->ha_fid.fid_pad != 0) {
238  			char handle_data[sizeof(struct fsal_fsid__)];
239  			int rc;
240  	
241  			*fsid_type = (enum fsid_type) (hdl->ha_fid.fid_pad - 1);
242  	
243  			memcpy(handle_data, &hdl->ha_fsid, sizeof(hdl->ha_fsid));
244  			memcpy(handle_data + sizeof(hdl->ha_fsid),
245  			       &hdl->ha_fid.fid_ino,
246  			       sizeof(hdl->ha_fid.fid_ino));
247  	
248  			rc = decode_fsid(handle_data,
249  					 sizeof(handle_data),
250  					 fsid,
251  					 *fsid_type);
252  			if (rc < 0) {
253  				errno = EINVAL;
254  				return rc;
255  			}
256  	
257  			return 0;
258  		}
259  	
260  		*fsid_type = FSID_TWO_UINT32;
261  		fsid->major = hdl->ha_fsid.val[0];
262  		fsid->minor = hdl->ha_fsid.val[1];
263  	
264  		return 0;
265  	}
266  	
267  	int vfs_encode_dummy_handle(vfs_file_handle_t *fh,
268  				    struct fsal_filesystem *fs)
269  	{
270  		xfs_handle_t *hdl = (xfs_handle_t *) fh->handle_data;
271  		char handle_data[sizeof(struct fsal_fsid__)];
272  		int rc;
273  	
274  		memset(handle_data, 0, sizeof(handle_data));
275  	
276  		/* Pack fsid into handle_data */
277  		rc = encode_fsid(handle_data,
278  				 sizeof(handle_data),
279  				 &fs->fsid,
280  				 fs->fsid_type);
281  	
282  		if (rc < 0) {
283  			errno = EINVAL;
284  			return rc;
285  		}
286  	
287  		assert(sizeof(handle_data) ==
288  		       (sizeof(hdl->ha_fsid) + sizeof(hdl->ha_fid.fid_ino)));
289  	
290  		memcpy(&hdl->ha_fsid, handle_data, sizeof(hdl->ha_fsid));
291  		memcpy(&hdl->ha_fid.fid_ino,
292  		       handle_data + sizeof(hdl->ha_fsid),
293  		       sizeof(hdl->ha_fid.fid_ino));
294  		hdl->ha_fid.fid_len = sizeof(xfs_handle_t) -
295  				      sizeof(xfs_fsid_t) -
296  				      sizeof(hdl->ha_fid.fid_len);
297  		hdl->ha_fid.fid_pad = fs->fsid_type + 1;
298  		hdl->ha_fid.fid_gen = 0;
299  		fh->handle_len = sizeof(*hdl);
300  	
301  		LogXFSHandle(fh);
302  	
303  		return 0;
304  	}
305  	
306  	bool vfs_is_dummy_handle(vfs_file_handle_t *fh)
307  	{
308  		xfs_handle_t *hdl = (xfs_handle_t *) fh->handle_data;
309  	
310  		return hdl->ha_fid.fid_pad != 0;
311  	}
312  	
313  	bool vfs_valid_handle(struct gsh_buffdesc *desc)
314  	{
315  		xfs_handle_t *hdl = (xfs_handle_t *) desc->addr;
316  		bool fsid_type_ok = false;
317  	
318  		if ((desc->addr == NULL) ||
319  		    (desc->len != sizeof(xfs_handle_t)))
320  			return false;
321  	
322  		if (isMidDebug(COMPONENT_FSAL)) {
323  			char buf[256] = "\0";
324  			struct display_buffer dspbuf = {sizeof(buf), buf, buf};
325  	
326  			(void) display_printf(&dspbuf,
327  					      "Handle len %d: fsid=0x%016"
328  					      PRIx32".0x%016"PRIx32
329  					      " fid_len=%"PRIu16
330  					      " fid_pad=%"PRIu16
331  					      " fid_gen=%"PRIu32
332  					      " fid_ino=%"PRIu64,
333  					      (int) desc->len,
334  					      hdl->ha_fsid.val[0],
335  					      hdl->ha_fsid.val[1],
336  					      hdl->ha_fid.fid_len,
337  					      hdl->ha_fid.fid_pad,
338  					      hdl->ha_fid.fid_gen,
339  					      hdl->ha_fid.fid_ino);
340  	
341  			LogMidDebug(COMPONENT_FSAL, "%s", buf);
342  		}
343  	
344  		if (hdl->ha_fid.fid_pad != 0) {
345  			switch ((enum fsid_type) (hdl->ha_fid.fid_pad - 1)) {
346  			case FSID_NO_TYPE:
347  			case FSID_ONE_UINT64:
348  			case FSID_MAJOR_64:
349  			case FSID_TWO_UINT64:
350  			case FSID_TWO_UINT32:
351  			case FSID_DEVICE:
352  				fsid_type_ok = true;
353  				break;
354  			}
355  	
356  			if (!fsid_type_ok) {
357  				LogDebug(COMPONENT_FSAL,
358  					 "FSID Type %02"PRIu16" invalid",
359  					 hdl->ha_fid.fid_pad - 1);
360  				return false;
361  			}
362  	
363  			if (hdl->ha_fid.fid_gen != 0)
364  				return false;
365  		}
366  	
367  		return hdl->ha_fid.fid_len == (sizeof(xfs_handle_t) -
368  					       sizeof(xfs_fsid_t) -
369  					       sizeof(hdl->ha_fid.fid_len));
370  	}
371  	
372  	int vfs_get_root_handle(struct vfs_filesystem *vfs_fs,
373  				struct vfs_fsal_export *exp)
374  	{
375  		enum fsid_type fsid_type;
376  		struct fsal_fsid__ fsid;
377  		int fd;
378  		int retval;
379  		void *data;
380  		size_t sz;
381  		vfs_file_handle_t *fh;
382  	
383  		vfs_alloc_handle(fh);
384  	
385  		if (path_to_fshandle(vfs_fs->fs->path, &data, &sz) < 0) {
386  			retval = errno;
387  			LogMajor(COMPONENT_FSAL,
388  				 "Export root %s could not be established for XFS error %s",
389  				 vfs_fs->fs->path, strerror(retval));
390  			return retval;
391  		}
392  	
393  		fd = open(vfs_fs->fs->path, O_RDONLY | O_DIRECTORY);
394  	
395  		if (fd < 0) {
396  			retval = errno;
397  			LogMajor(COMPONENT_FSAL,
398  				 "Could not open XFS mount point %s: rc = %s (%d)",
399  				 vfs_fs->fs->path, strerror(retval), retval);
400  			return retval;
401  		}
402  	
403  		retval = vfs_fd_to_handle(fd, vfs_fs->fs, fh);
404  	
405  		if (retval != 0) {
406  			retval = errno;
407  			LogMajor(COMPONENT_FSAL,
408  				 "Get root handle for %s failed with %s (%d)",
409  				 vfs_fs->fs->path, strerror(retval), retval);
410  			goto errout;
411  		}
412  	
413  		/* Extract fsid from the root handle and re-index the filesystem
414  		 * using it. This is because the file handle already has an fsid in
415  		 * it.
416  		 */
417  		(void) vfs_extract_fsid(fh, &fsid_type, &fsid);
418  	
419  		retval = re_index_fs_fsid(vfs_fs->fs, fsid_type, &fsid);
420  	
421  		if (retval < 0) {
422  			LogCrit(COMPONENT_FSAL,
423  				"Could not re-index XFS file system fsid for %s",
424  				vfs_fs->fs->path);
425  			retval = -retval;
426  		}
427  	
428  	errout:
429  	
430  		close(fd);
431  	
432  		return retval;
433  	}
434  	
435