smbd: Add reparse tag to smb3_posix_cc_info
[samba.git] / source3 / modules / vfs_glusterfs_fuse.c
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 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         char *found_name = NULL;
37
38         if (strlen(name) >= GLUSTER_NAME_MAX) {
39                 return NT_STATUS_OBJECT_NAME_INVALID;
40         }
41
42         snprintf(key_buf, GLUSTER_NAME_MAX + 64,
43                  "glusterfs.get_real_filename:%s", name);
44
45         dirfd = openat(fsp_get_pathref_fd(dirfsp), ".", O_RDONLY);
46         if (dirfd == -1) {
47                 NTSTATUS status = map_nt_error_from_unix(errno);
48                 DBG_DEBUG("Could not open '.' in %s: %s\n",
49                           fsp_str_dbg(dirfsp),
50                           strerror(errno));
51                 return status;
52         }
53
54         ret = fgetxattr(dirfd, key_buf, val_buf, GLUSTER_NAME_MAX + 1);
55         close(dirfd);
56         if (ret == -1) {
57                 if (errno == ENOATTR) {
58                         errno = ENOENT;
59                 }
60                 return map_nt_error_from_unix(errno);
61         }
62
63         found_name = talloc_strdup(mem_ctx, val_buf);
64         if (found_name == NULL) {
65                 return NT_STATUS_NO_MEMORY;
66         }
67         *_found_name = found_name;
68         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 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         for (value = 0x238F13AFLL * len, i=0; i < len; i++)
89                 value = (value + (((uint64_t)s[i]) << (i*5 % 24)));
90
91         return (1103515243LL * value + 12345LL);
92 }
93
94 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         data->num_mapped_devices = 0;
101         TALLOC_FREE(data->mapped_devices);
102
103         f = setmntent("/etc/mtab", "r");
104         if (!f) {
105                 return;
106         }
107
108         while ((m = getmntent(f))) {
109                 struct stat st;
110                 char *p;
111                 uint64_t mapped_device;
112
113                 if (stat(m->mnt_dir, &st) != 0) {
114                         /* TODO: log? */
115                         continue;
116                 }
117
118                 /* strip the host part off of the fsname */
119                 p = strrchr(m->mnt_fsname, ':');
120                 if (p == NULL) {
121                         p = m->mnt_fsname;
122                 } else {
123                         /* TODO: consider the case of '' ? */
124                         p++;
125                 }
126
127                 mapped_device = vfs_glusterfs_fuse_uint64_hash(
128                                                 (const uint8_t *)p,
129                                                 strlen(p));
130
131                 data->mapped_devices = talloc_realloc(data,
132                                                 data->mapped_devices,
133                                                 struct device_mapping_entry,
134                                                 data->num_mapped_devices + 1);
135                 if (data->mapped_devices == NULL) {
136                         goto nomem;
137                 }
138
139                 data->mapped_devices[data->num_mapped_devices].device =
140                                                                 st.st_dev;
141                 data->mapped_devices[data->num_mapped_devices].mapped_device =
142                                                                 mapped_device;
143
144                 data->num_mapped_devices++;
145         }
146
147         endmntent(f);
148         return;
149
150 nomem:
151         data->num_mapped_devices = 0;
152         TALLOC_FREE(data->mapped_devices);
153
154         endmntent(f);
155         return;
156 }
157
158 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         for (i = 0; i < data->num_mapped_devices; i++) {
166                 if (data->mapped_devices[i].device == device) {
167                         *mapped_device = data->mapped_devices[i].mapped_device;
168                         return 0;
169                 }
170         }
171
172         return -1;
173 }
174
175 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         ret = vfs_glusterfs_fuse_map_device_cached(data, device, mapped_device);
183         if (ret == 0) {
184                 return 0;
185         }
186
187         vfs_glusterfs_fuse_load_devices(data);
188
189         ret = vfs_glusterfs_fuse_map_device_cached(data, device, mapped_device);
190
191         return ret;
192 }
193
194 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         ZERO_STRUCT(id);
204
205         id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, sbuf);
206
207         SMB_VFS_HANDLE_GET_DATA(handle, data,
208                                 struct vfs_glusterfs_fuse_handle_data,
209                                 return id);
210
211         ret = vfs_glusterfs_fuse_map_device(data, sbuf->st_ex_dev,
212                                             &mapped_device);
213         if (ret == 0) {
214                 id.devid = mapped_device;
215         } else {
216                 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         DBG_DEBUG("Returning dev [%jx] inode [%jx]\n",
223                   (uintmax_t)id.devid, (uintmax_t)id.inode);
224
225         return id;
226 }
227
228 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         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
233
234         if (ret < 0) {
235                 return ret;
236         }
237
238         data = talloc_zero(handle->conn, struct vfs_glusterfs_fuse_handle_data);
239         if (data == NULL) {
240                 DBG_ERR("talloc_zero() failed.\n");
241                 SMB_VFS_NEXT_DISCONNECT(handle);
242                 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         vfs_glusterfs_fuse_load_devices(data);
250
251         SMB_VFS_HANDLE_SET_DATA(handle, data, NULL,
252                                 struct vfs_glusterfs_fuse_handle_data,
253                                 return -1);
254
255         DBG_DEBUG("vfs_glusterfs_fuse_connect(): connected to service[%s]\n",
256                   service);
257
258         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 NTSTATUS vfs_glusterfs_fuse_init(TALLOC_CTX *ctx)
270 {
271         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
272                                 "glusterfs_fuse", &glusterfs_fuse_fns);
273 }