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