s3-includes: only include system/filesys.h when needed.
[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 "system/filesys.h"
24
25 static int vfs_fileid_debug_level = DBGC_VFS;
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS vfs_fileid_debug_level
29
30 struct fileid_mount_entry {
31         SMB_DEV_T device;
32         const char *mnt_fsname;
33         fsid_t fsid;
34         uint64_t devid;
35 };
36
37 struct fileid_handle_data {
38         uint64_t (*device_mapping_fn)(struct fileid_handle_data *data,
39                                       SMB_DEV_T dev);
40         unsigned num_mount_entries;
41         struct fileid_mount_entry *mount_entries;
42 };
43
44 /* load all the mount entries from the mtab */
45 static void fileid_load_mount_entries(struct fileid_handle_data *data)
46 {
47         FILE *f;
48         struct mntent *m;
49
50         data->num_mount_entries = 0;
51         TALLOC_FREE(data->mount_entries);
52
53         f = setmntent("/etc/mtab", "r");
54         if (!f) return;
55
56         while ((m = getmntent(f))) {
57                 struct stat st;
58                 struct statfs sfs;
59                 struct fileid_mount_entry *cur;
60
61                 if (stat(m->mnt_dir, &st) != 0) continue;
62                 if (statfs(m->mnt_dir, &sfs) != 0) continue;
63
64                 if (strncmp(m->mnt_fsname, "/dev/", 5) == 0) {
65                         m->mnt_fsname += 5;
66                 }
67
68                 data->mount_entries = TALLOC_REALLOC_ARRAY(data,
69                                                            data->mount_entries,
70                                                            struct fileid_mount_entry,
71                                                            data->num_mount_entries+1);
72                 if (data->mount_entries == NULL) {
73                         goto nomem;
74                 }
75
76                 cur = &data->mount_entries[data->num_mount_entries];
77                 cur->device     = st.st_dev;
78                 cur->mnt_fsname = talloc_strdup(data->mount_entries,
79                                                 m->mnt_fsname);
80                 if (!cur->mnt_fsname) goto nomem;
81                 cur->fsid       = sfs.f_fsid;
82                 cur->devid      = (uint64_t)-1;
83
84                 data->num_mount_entries++;
85         }
86         endmntent(f);
87         return;
88         
89 nomem:
90         if (f) endmntent(f);
91
92         data->num_mount_entries = 0;
93         TALLOC_FREE(data->mount_entries);
94
95         return;
96 }
97
98 /* find a mount entry given a dev_t */
99 static struct fileid_mount_entry *fileid_find_mount_entry(struct fileid_handle_data *data,
100                                                           SMB_DEV_T dev)
101 {
102         int i;
103
104         if (data->num_mount_entries == 0) {
105                 fileid_load_mount_entries(data);
106         }
107         for (i=0;i<data->num_mount_entries;i++) {
108                 if (data->mount_entries[i].device == dev) {
109                         return &data->mount_entries[i];
110                 }
111         }
112         /* 2nd pass after reloading */
113         fileid_load_mount_entries(data);
114         for (i=0;i<data->num_mount_entries;i++) {
115                 if (data->mount_entries[i].device == dev) {
116                         return &data->mount_entries[i];
117                 }
118         }       
119         return NULL;
120 }
121
122
123 /* a 64 bit hash, based on the one in tdb */
124 static uint64_t fileid_uint64_hash(const uint8_t *s, size_t len)
125 {
126         uint64_t value; /* Used to compute the hash value.  */
127         uint32_t i;     /* Used to cycle through random values. */
128
129         /* Set the initial value from the key size. */
130         for (value = 0x238F13AFLL * len, i=0; i < len; i++)
131                 value = (value + (s[i] << (i*5 % 24)));
132
133         return (1103515243LL * value + 12345LL);
134 }
135
136 /* a device mapping using a fsname */
137 static uint64_t fileid_device_mapping_fsname(struct fileid_handle_data *data,
138                                              SMB_DEV_T dev)
139 {
140         struct fileid_mount_entry *m;
141
142         m = fileid_find_mount_entry(data, dev);
143         if (!m) return dev;
144
145         if (m->devid == (uint64_t)-1) {
146                 m->devid = fileid_uint64_hash((uint8_t *)m->mnt_fsname,
147                                               strlen(m->mnt_fsname));
148         }
149
150         return m->devid;
151 }
152
153 /* device mapping functions using a fsid */
154 static uint64_t fileid_device_mapping_fsid(struct fileid_handle_data *data,
155                                            SMB_DEV_T dev)
156 {
157         struct fileid_mount_entry *m;
158
159         m = fileid_find_mount_entry(data, dev);
160         if (!m) return dev;
161
162         if (m->devid == (uint64_t)-1) {
163                 if (sizeof(fsid_t) > sizeof(uint64_t)) {
164                         m->devid = fileid_uint64_hash((uint8_t *)&m->fsid,
165                                                       sizeof(m->fsid));
166                 } else {
167                         union {
168                                 uint64_t ret;
169                                 fsid_t fsid;
170                         } u;
171                         ZERO_STRUCT(u);
172                         u.fsid = m->fsid;
173                         m->devid = u.ret;
174                 }
175         }
176
177         return m->devid;
178 }
179
180 static int fileid_connect(struct vfs_handle_struct *handle,
181                           const char *service, const char *user)
182 {
183         struct fileid_handle_data *data;
184         const char *algorithm;
185         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
186
187         if (ret < 0) {
188                 return ret;
189         }
190
191         data = talloc_zero(handle->conn, struct fileid_handle_data);
192         if (!data) {
193                 SMB_VFS_NEXT_DISCONNECT(handle);
194                 DEBUG(0, ("talloc_zero() failed\n"));
195                 return -1;
196         }
197
198         /*
199          * "fileid:mapping" is only here as fallback for old setups
200          * "fileid:algorithm" is the option new setups should use
201          */
202         algorithm = lp_parm_const_string(SNUM(handle->conn),
203                                          "fileid", "mapping",
204                                          "fsname");
205         algorithm = lp_parm_const_string(SNUM(handle->conn),
206                                          "fileid", "algorithm",
207                                          algorithm);
208         if (strcmp("fsname", algorithm) == 0) {
209                 data->device_mapping_fn = fileid_device_mapping_fsname;
210         } else if (strcmp("fsid", algorithm) == 0) {
211                 data->device_mapping_fn = fileid_device_mapping_fsid;
212         } else {
213                 SMB_VFS_NEXT_DISCONNECT(handle);
214                 DEBUG(0,("fileid_connect(): unknown algorithm[%s]\n", algorithm));
215                 return -1;
216         }
217
218         SMB_VFS_HANDLE_SET_DATA(handle, data, NULL,
219                                 struct fileid_handle_data,
220                                 return -1);
221
222         DEBUG(10, ("fileid_connect(): connect to service[%s] with algorithm[%s]\n",
223                 service, algorithm));
224
225         return 0;
226 }
227
228 static void fileid_disconnect(struct vfs_handle_struct *handle)
229 {
230         DEBUG(10,("fileid_disconnect() connect to service[%s].\n",
231                 lp_servicename(SNUM(handle->conn))));
232
233         SMB_VFS_NEXT_DISCONNECT(handle);
234 }
235
236 static struct file_id fileid_file_id_create(struct vfs_handle_struct *handle,
237                                             const SMB_STRUCT_STAT *sbuf)
238 {
239         struct fileid_handle_data *data;
240         struct file_id id;
241
242         ZERO_STRUCT(id);
243
244         SMB_VFS_HANDLE_GET_DATA(handle, data,
245                                 struct fileid_handle_data,
246                                 return id);
247
248         id.devid        = data->device_mapping_fn(data, sbuf->st_ex_dev);
249         id.inode        = sbuf->st_ex_ino;
250
251         return id;
252 }
253
254 static struct vfs_fn_pointers vfs_fileid_fns = {
255         .connect_fn = fileid_connect,
256         .disconnect = fileid_disconnect,
257         .file_id_create = fileid_file_id_create
258 };
259
260 NTSTATUS vfs_fileid_init(void);
261 NTSTATUS vfs_fileid_init(void)
262 {
263         NTSTATUS ret;
264
265         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fileid",
266                                &vfs_fileid_fns);
267         if (!NT_STATUS_IS_OK(ret)) {
268                 return ret;
269         }
270
271         vfs_fileid_debug_level = debug_add_class("fileid");
272         if (vfs_fileid_debug_level == -1) {
273                 vfs_fileid_debug_level = DBGC_VFS;
274                 DEBUG(0, ("vfs_fileid: Couldn't register custom debugging class!\n"));
275         } else {
276                 DEBUG(10, ("vfs_fileid: Debug class number of 'fileid': %d\n", vfs_fileid_debug_level));
277         }
278
279         return ret;
280 }