Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Copyright (c) 2019 Guenther Deschner <gd@samba.org>
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "includes.h"
21 : #include "smbd/smbd.h"
22 : #include "system/filesys.h"
23 :
24 : #define GLUSTER_NAME_MAX 255
25 :
26 0 : static NTSTATUS vfs_gluster_fuse_get_real_filename_at(
27 : struct vfs_handle_struct *handle,
28 : struct files_struct *dirfsp,
29 : const char *name,
30 : TALLOC_CTX *mem_ctx,
31 : char **_found_name)
32 : {
33 : int ret, dirfd;
34 : char key_buf[GLUSTER_NAME_MAX + 64];
35 : char val_buf[GLUSTER_NAME_MAX + 1];
36 0 : char *found_name = NULL;
37 :
38 0 : if (strlen(name) >= GLUSTER_NAME_MAX) {
39 0 : return NT_STATUS_OBJECT_NAME_INVALID;
40 : }
41 :
42 0 : snprintf(key_buf, GLUSTER_NAME_MAX + 64,
43 : "glusterfs.get_real_filename:%s", name);
44 :
45 0 : dirfd = openat(fsp_get_pathref_fd(dirfsp), ".", O_RDONLY);
46 0 : if (dirfd == -1) {
47 0 : NTSTATUS status = map_nt_error_from_unix(errno);
48 0 : DBG_DEBUG("Could not open '.' in %s: %s\n",
49 : fsp_str_dbg(dirfsp),
50 : strerror(errno));
51 0 : return status;
52 : }
53 :
54 0 : ret = fgetxattr(dirfd, key_buf, val_buf, GLUSTER_NAME_MAX + 1);
55 0 : close(dirfd);
56 0 : if (ret == -1) {
57 0 : if (errno == ENOATTR) {
58 0 : errno = ENOENT;
59 : }
60 0 : return map_nt_error_from_unix(errno);
61 : }
62 :
63 0 : found_name = talloc_strdup(mem_ctx, val_buf);
64 0 : if (found_name == NULL) {
65 0 : return NT_STATUS_NO_MEMORY;
66 : }
67 0 : *_found_name = found_name;
68 0 : return NT_STATUS_OK;
69 : }
70 :
71 : struct device_mapping_entry {
72 : SMB_DEV_T device; /* the local device, for reference */
73 : uint64_t mapped_device; /* the mapped device */
74 : };
75 :
76 : struct vfs_glusterfs_fuse_handle_data {
77 : unsigned num_mapped_devices;
78 : struct device_mapping_entry *mapped_devices;
79 : };
80 :
81 : /* a 64 bit hash, based on the one in tdb, copied from vfs_fileied */
82 0 : static uint64_t vfs_glusterfs_fuse_uint64_hash(const uint8_t *s, size_t len)
83 : {
84 : uint64_t value; /* Used to compute the hash value. */
85 : uint32_t i; /* Used to cycle through random values. */
86 :
87 : /* Set the initial value from the key size. */
88 0 : for (value = 0x238F13AFLL * len, i=0; i < len; i++)
89 0 : value = (value + (((uint64_t)s[i]) << (i*5 % 24)));
90 :
91 0 : return (1103515243LL * value + 12345LL);
92 : }
93 :
94 0 : static void vfs_glusterfs_fuse_load_devices(
95 : struct vfs_glusterfs_fuse_handle_data *data)
96 : {
97 : FILE *f;
98 : struct mntent *m;
99 :
100 0 : data->num_mapped_devices = 0;
101 0 : TALLOC_FREE(data->mapped_devices);
102 :
103 0 : f = setmntent("/etc/mtab", "r");
104 0 : if (!f) {
105 0 : return;
106 : }
107 :
108 0 : while ((m = getmntent(f))) {
109 : struct stat st;
110 : char *p;
111 : uint64_t mapped_device;
112 :
113 0 : if (stat(m->mnt_dir, &st) != 0) {
114 : /* TODO: log? */
115 0 : continue;
116 : }
117 :
118 : /* strip the host part off of the fsname */
119 0 : p = strrchr(m->mnt_fsname, ':');
120 0 : if (p == NULL) {
121 0 : p = m->mnt_fsname;
122 : } else {
123 : /* TODO: consider the case of '' ? */
124 0 : p++;
125 : }
126 :
127 0 : mapped_device = vfs_glusterfs_fuse_uint64_hash(
128 : (const uint8_t *)p,
129 : strlen(p));
130 :
131 0 : data->mapped_devices = talloc_realloc(data,
132 : data->mapped_devices,
133 : struct device_mapping_entry,
134 : data->num_mapped_devices + 1);
135 0 : if (data->mapped_devices == NULL) {
136 0 : goto nomem;
137 : }
138 :
139 0 : data->mapped_devices[data->num_mapped_devices].device =
140 0 : st.st_dev;
141 0 : data->mapped_devices[data->num_mapped_devices].mapped_device =
142 : mapped_device;
143 :
144 0 : data->num_mapped_devices++;
145 : }
146 :
147 0 : endmntent(f);
148 0 : return;
149 :
150 0 : nomem:
151 0 : data->num_mapped_devices = 0;
152 0 : TALLOC_FREE(data->mapped_devices);
153 :
154 0 : endmntent(f);
155 0 : return;
156 : }
157 :
158 0 : static int vfs_glusterfs_fuse_map_device_cached(
159 : struct vfs_glusterfs_fuse_handle_data *data,
160 : SMB_DEV_T device,
161 : uint64_t *mapped_device)
162 : {
163 : unsigned i;
164 :
165 0 : for (i = 0; i < data->num_mapped_devices; i++) {
166 0 : if (data->mapped_devices[i].device == device) {
167 0 : *mapped_device = data->mapped_devices[i].mapped_device;
168 0 : return 0;
169 : }
170 : }
171 :
172 0 : return -1;
173 : }
174 :
175 0 : static int vfs_glusterfs_fuse_map_device(
176 : struct vfs_glusterfs_fuse_handle_data *data,
177 : SMB_DEV_T device,
178 : uint64_t *mapped_device)
179 : {
180 : int ret;
181 :
182 0 : ret = vfs_glusterfs_fuse_map_device_cached(data, device, mapped_device);
183 0 : if (ret == 0) {
184 0 : return 0;
185 : }
186 :
187 0 : vfs_glusterfs_fuse_load_devices(data);
188 :
189 0 : ret = vfs_glusterfs_fuse_map_device_cached(data, device, mapped_device);
190 :
191 0 : return ret;
192 : }
193 :
194 0 : static struct file_id vfs_glusterfs_fuse_file_id_create(
195 : struct vfs_handle_struct *handle,
196 : const SMB_STRUCT_STAT *sbuf)
197 : {
198 : struct vfs_glusterfs_fuse_handle_data *data;
199 : struct file_id id;
200 : uint64_t mapped_device;
201 : int ret;
202 :
203 0 : ZERO_STRUCT(id);
204 :
205 0 : id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, sbuf);
206 :
207 0 : SMB_VFS_HANDLE_GET_DATA(handle, data,
208 : struct vfs_glusterfs_fuse_handle_data,
209 : return id);
210 :
211 0 : ret = vfs_glusterfs_fuse_map_device(data, sbuf->st_ex_dev,
212 : &mapped_device);
213 0 : if (ret == 0) {
214 0 : id.devid = mapped_device;
215 : } else {
216 0 : DBG_WARNING("Failed to map device [%jx], falling back to "
217 : "standard file_id [%jx]\n",
218 : (uintmax_t)sbuf->st_ex_dev,
219 : (uintmax_t)id.devid);
220 : }
221 :
222 0 : DBG_DEBUG("Returning dev [%jx] inode [%jx]\n",
223 : (uintmax_t)id.devid, (uintmax_t)id.inode);
224 :
225 0 : return id;
226 : }
227 :
228 0 : static int vfs_glusterfs_fuse_connect(struct vfs_handle_struct *handle,
229 : const char *service, const char *user)
230 : {
231 : struct vfs_glusterfs_fuse_handle_data *data;
232 0 : int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
233 :
234 0 : if (ret < 0) {
235 0 : return ret;
236 : }
237 :
238 0 : data = talloc_zero(handle->conn, struct vfs_glusterfs_fuse_handle_data);
239 0 : if (data == NULL) {
240 0 : DBG_ERR("talloc_zero() failed.\n");
241 0 : SMB_VFS_NEXT_DISCONNECT(handle);
242 0 : return -1;
243 : }
244 :
245 : /*
246 : * Fill the cache in the tree connect, so that the first file/dir access
247 : * has chances of being fast...
248 : */
249 0 : vfs_glusterfs_fuse_load_devices(data);
250 :
251 0 : SMB_VFS_HANDLE_SET_DATA(handle, data, NULL,
252 : struct vfs_glusterfs_fuse_handle_data,
253 : return -1);
254 :
255 0 : DBG_DEBUG("vfs_glusterfs_fuse_connect(): connected to service[%s]\n",
256 : service);
257 :
258 0 : return 0;
259 : }
260 :
261 : struct vfs_fn_pointers glusterfs_fuse_fns = {
262 :
263 : .connect_fn = vfs_glusterfs_fuse_connect,
264 : .get_real_filename_at_fn = vfs_gluster_fuse_get_real_filename_at,
265 : .file_id_create_fn = vfs_glusterfs_fuse_file_id_create,
266 : };
267 :
268 : static_decl_vfs;
269 27 : NTSTATUS vfs_glusterfs_fuse_init(TALLOC_CTX *ctx)
270 : {
271 27 : return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
272 : "glusterfs_fuse", &glusterfs_fuse_fns);
273 : }
|