loadparm: make the source3/ lp_ functions take an explicit TALLOC_CTX *.
[kai/samba.git] / source3 / modules / vfs_fileid.c
1 /*
2  * VFS module to alter the algorithm to calculate
3  * the struct file_id used as key for the share mode
4  * and byte range locking db's.
5  *
6  * Copyright (C) 2007, Stefan Metzmacher
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "includes.h"
23 #include "smbd/smbd.h"
24 #include "system/filesys.h"
25
26 static int vfs_fileid_debug_level = DBGC_VFS;
27
28 #undef DBGC_CLASS
29 #define DBGC_CLASS vfs_fileid_debug_level
30
31 struct fileid_mount_entry {
32         SMB_DEV_T device;
33         const char *mnt_fsname;
34         fsid_t fsid;
35         uint64_t devid;
36 };
37
38 struct fileid_handle_data {
39         uint64_t (*device_mapping_fn)(struct fileid_handle_data *data,
40                                       SMB_DEV_T dev);
41         unsigned num_mount_entries;
42         struct fileid_mount_entry *mount_entries;
43 };
44
45 /* load all the mount entries from the mtab */
46 static void fileid_load_mount_entries(struct fileid_handle_data *data)
47 {
48         FILE *f;
49         struct mntent *m;
50
51         data->num_mount_entries = 0;
52         TALLOC_FREE(data->mount_entries);
53
54         f = setmntent("/etc/mtab", "r");
55         if (!f) return;
56
57         while ((m = getmntent(f))) {
58                 struct stat st;
59                 struct statfs sfs;
60                 struct fileid_mount_entry *cur;
61
62                 if (stat(m->mnt_dir, &st) != 0) continue;
63                 if (statfs(m->mnt_dir, &sfs) != 0) continue;
64
65                 if (strncmp(m->mnt_fsname, "/dev/", 5) == 0) {
66                         m->mnt_fsname += 5;
67                 }
68
69                 data->mount_entries = talloc_realloc(data,
70                                                            data->mount_entries,
71                                                            struct fileid_mount_entry,
72                                                            data->num_mount_entries+1);
73                 if (data->mount_entries == NULL) {
74                         goto nomem;
75                 }
76
77                 cur = &data->mount_entries[data->num_mount_entries];
78                 cur->device     = st.st_dev;
79                 cur->mnt_fsname = talloc_strdup(data->mount_entries,
80                                                 m->mnt_fsname);
81                 if (!cur->mnt_fsname) goto nomem;
82                 cur->fsid       = sfs.f_fsid;
83                 cur->devid      = (uint64_t)-1;
84
85                 data->num_mount_entries++;
86         }
87         endmntent(f);
88         return;
89         
90 nomem:
91         if (f) endmntent(f);
92
93         data->num_mount_entries = 0;
94         TALLOC_FREE(data->mount_entries);
95
96         return;
97 }
98
99 /* find a mount entry given a dev_t */
100 static struct fileid_mount_entry *fileid_find_mount_entry(struct fileid_handle_data *data,
101                                                           SMB_DEV_T dev)
102 {
103         int i;
104
105         if (data->num_mount_entries == 0) {
106                 fileid_load_mount_entries(data);
107         }
108         for (i=0;i<data->num_mount_entries;i++) {
109                 if (data->mount_entries[i].device == dev) {
110                         return &data->mount_entries[i];
111                 }
112         }
113         /* 2nd pass after reloading */
114         fileid_load_mount_entries(data);
115         for (i=0;i<data->num_mount_entries;i++) {
116                 if (data->mount_entries[i].device == dev) {
117                         return &data->mount_entries[i];
118                 }
119         }       
120         return NULL;
121 }
122
123
124 /* a 64 bit hash, based on the one in tdb */
125 static uint64_t fileid_uint64_hash(const uint8_t *s, size_t len)
126 {
127         uint64_t value; /* Used to compute the hash value.  */
128         uint32_t i;     /* Used to cycle through random values. */
129
130         /* Set the initial value from the key size. */
131         for (value = 0x238F13AFLL * len, i=0; i < len; i++)
132                 value = (value + (s[i] << (i*5 % 24)));
133
134         return (1103515243LL * value + 12345LL);
135 }
136
137 /* a device mapping using a fsname */
138 static uint64_t fileid_device_mapping_fsname(struct fileid_handle_data *data,
139                                              SMB_DEV_T dev)
140 {
141         struct fileid_mount_entry *m;
142
143         m = fileid_find_mount_entry(data, dev);
144         if (!m) return dev;
145
146         if (m->devid == (uint64_t)-1) {
147                 m->devid = fileid_uint64_hash((const uint8_t *)m->mnt_fsname,
148                                               strlen(m->mnt_fsname));
149         }
150
151         return m->devid;
152 }
153
154 /* device mapping functions using a fsid */
155 static uint64_t fileid_device_mapping_fsid(struct fileid_handle_data *data,
156                                            SMB_DEV_T dev)
157 {
158         struct fileid_mount_entry *m;
159
160         m = fileid_find_mount_entry(data, dev);
161         if (!m) return dev;
162
163         if (m->devid == (uint64_t)-1) {
164                 if (sizeof(fsid_t) > sizeof(uint64_t)) {
165                         m->devid = fileid_uint64_hash((uint8_t *)&m->fsid,
166                                                       sizeof(m->fsid));
167                 } else {
168                         union {
169                                 uint64_t ret;
170                                 fsid_t fsid;
171                         } u;
172                         ZERO_STRUCT(u);
173                         u.fsid = m->fsid;
174                         m->devid = u.ret;
175                 }
176         }
177
178         return m->devid;
179 }
180
181 static int fileid_connect(struct vfs_handle_struct *handle,
182                           const char *service, const char *user)
183 {
184         struct fileid_handle_data *data;
185         const char *algorithm;
186         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
187
188         if (ret < 0) {
189                 return ret;
190         }
191
192         data = talloc_zero(handle->conn, struct fileid_handle_data);
193         if (!data) {
194                 SMB_VFS_NEXT_DISCONNECT(handle);
195                 DEBUG(0, ("talloc_zero() failed\n"));
196                 return -1;
197         }
198
199         /*
200          * "fileid:mapping" is only here as fallback for old setups
201          * "fileid:algorithm" is the option new setups should use
202          */
203         algorithm = lp_parm_const_string(SNUM(handle->conn),
204                                          "fileid", "mapping",
205                                          "fsname");
206         algorithm = lp_parm_const_string(SNUM(handle->conn),
207                                          "fileid", "algorithm",
208                                          algorithm);
209         if (strcmp("fsname", algorithm) == 0) {
210                 data->device_mapping_fn = fileid_device_mapping_fsname;
211         } else if (strcmp("fsid", algorithm) == 0) {
212                 data->device_mapping_fn = fileid_device_mapping_fsid;
213         } else {
214                 SMB_VFS_NEXT_DISCONNECT(handle);
215                 DEBUG(0,("fileid_connect(): unknown algorithm[%s]\n", algorithm));
216                 return -1;
217         }
218
219         SMB_VFS_HANDLE_SET_DATA(handle, data, NULL,
220                                 struct fileid_handle_data,
221                                 return -1);
222
223         DEBUG(10, ("fileid_connect(): connect to service[%s] with algorithm[%s]\n",
224                 service, algorithm));
225
226         return 0;
227 }
228
229 static void fileid_disconnect(struct vfs_handle_struct *handle)
230 {
231         DEBUG(10,("fileid_disconnect() connect to service[%s].\n",
232                   lp_servicename(talloc_tos(), SNUM(handle->conn))));
233
234         SMB_VFS_NEXT_DISCONNECT(handle);
235 }
236
237 static struct file_id fileid_file_id_create(struct vfs_handle_struct *handle,
238                                             const SMB_STRUCT_STAT *sbuf)
239 {
240         struct fileid_handle_data *data;
241         struct file_id id;
242
243         ZERO_STRUCT(id);
244
245         SMB_VFS_HANDLE_GET_DATA(handle, data,
246                                 struct fileid_handle_data,
247                                 return id);
248
249         id.devid        = data->device_mapping_fn(data, sbuf->st_ex_dev);
250         id.inode        = sbuf->st_ex_ino;
251
252         return id;
253 }
254
255 static struct vfs_fn_pointers vfs_fileid_fns = {
256         .connect_fn = fileid_connect,
257         .disconnect_fn = fileid_disconnect,
258         .file_id_create_fn = fileid_file_id_create
259 };
260
261 NTSTATUS vfs_fileid_init(void);
262 NTSTATUS vfs_fileid_init(void)
263 {
264         NTSTATUS ret;
265
266         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fileid",
267                                &vfs_fileid_fns);
268         if (!NT_STATUS_IS_OK(ret)) {
269                 return ret;
270         }
271
272         vfs_fileid_debug_level = debug_add_class("fileid");
273         if (vfs_fileid_debug_level == -1) {
274                 vfs_fileid_debug_level = DBGC_VFS;
275                 DEBUG(0, ("vfs_fileid: Couldn't register custom debugging class!\n"));
276         } else {
277                 DEBUG(10, ("vfs_fileid: Debug class number of 'fileid': %d\n", vfs_fileid_debug_level));
278         }
279
280         return ret;
281 }