87a386307c1c245639bab6d38c2e2d477723cac4
[vlendec/samba-autobuild/.git] / source3 / libsmb / smb_share_modes.c
1 /*
2    Samba share mode database library external interface library.
3    Used by non-Samba products needing access to the Samba share mode db.
4                                                                                                                                   
5    Copyright (C) Jeremy Allison 2005.
6                                                                                                                                   
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2 of the License, or (at your option) any later version.
11                                                                                                                                   
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16                                                                                                                                   
17    You should have received a copy of the GNU Lesser General Public
18    License along with this library; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21
22 #include "includes.h"
23 #include "smb_share_modes.h"
24
25 /* Remove the paranoid malloc checker. */
26 #ifdef malloc
27 #undef malloc
28 #endif
29
30 /*
31  * open/close sharemode database.
32  */
33
34 struct smbdb_ctx *smb_share_mode_db_open(const char *db_path)
35 {
36         struct smbdb_ctx *smb_db = (struct smbdb_ctx *)malloc(sizeof(struct smbdb_ctx));
37
38         if (!smb_db) {
39                 return NULL;
40         }
41
42         memset(smb_db, '\0', sizeof(struct smbdb_ctx));
43
44         smb_db->smb_tdb = tdb_open(db_path,
45                                 0, TDB_DEFAULT|TDB_CLEAR_IF_FIRST,
46                                 O_RDWR|O_CREAT,
47                                 0644);
48
49         if (!smb_db->smb_tdb) {
50                 free(smb_db);
51                 return NULL;
52         }
53
54         /* Should check that this is the correct version.... */
55         return smb_db;
56 }
57
58 int smb_share_mode_db_close(struct smbdb_ctx *db_ctx)
59 {
60         int ret = tdb_close(db_ctx->smb_tdb);
61         free(db_ctx);
62         return ret;
63 }
64
65 static TDB_DATA get_locking_key(uint64_t dev, uint64_t ino)
66 {
67         static struct locking_key lk;
68         TDB_DATA ld;
69
70         memset(&lk, '\0', sizeof(struct locking_key));
71         lk.dev = (SMB_DEV_T)dev;
72         lk.inode = (SMB_INO_T)ino;
73         ld.dptr = (char *)&lk;
74         ld.dsize = sizeof(lk);
75         return ld;
76 }
77
78 /*
79  * lock/unlock entry in sharemode database.
80  */
81
82 int smb_lock_share_mode_entry(struct smbdb_ctx *db_ctx,
83                                 uint64_t dev,
84                                 uint64_t ino)
85 {
86         return tdb_chainlock(db_ctx->smb_tdb, get_locking_key(dev, ino));
87 }
88                                                                                                                                   
89 int smb_unlock_share_mode_entry(struct smbdb_ctx *db_ctx,
90                                 uint64_t dev,
91                                 uint64_t ino)
92 {
93         return tdb_chainunlock(db_ctx->smb_tdb, get_locking_key(dev, ino));
94 }
95
96 /* Internal structure of Samba share mode db. */
97 /* FIXME ! This should be moved into a Samba include file. */
98
99 struct locking_data {
100         union {
101                 struct {
102                         int num_share_mode_entries;
103                         BOOL delete_on_close;
104                 } s;
105                 share_mode_entry dummy; /* Needed for alignment. */
106         } u;
107         /* the following two entries are implicit
108            share_mode_entry modes[num_share_mode_entries];
109            char file_name[];
110         */
111 };
112
113 /*
114  * Check if an external smb_share_mode_entry and an internal share_mode entry match.
115  */
116
117 static int share_mode_entry_equal(const struct smb_share_mode_entry *e_entry, const share_mode_entry *entry)
118 {
119         return (e_entry->pid == entry->pid &&
120                 e_entry->file_id == (uint32_t)entry->share_file_id &&
121                 e_entry->open_time.tv_sec == entry->time.tv_sec &&
122                 e_entry->open_time.tv_usec == entry->time.tv_usec &&
123                 e_entry->share_access == (uint32_t)entry->share_access &&
124                 e_entry->access_mask == (uint32_t)entry->access_mask &&
125                 e_entry->dev == (uint64_t)entry->dev && 
126                 e_entry->ino == (uint64_t)entry->inode);
127 }
128
129 /*
130  * Create an internal Samba share_mode entry from an external smb_share_mode_entry.
131  */
132
133 static void create_share_mode_entry(share_mode_entry *out, const struct smb_share_mode_entry *in)
134 {
135         memset(out, '\0', sizeof(share_mode_entry));
136
137         out->pid = in->pid;
138         out->share_file_id = (unsigned long)in->file_id;
139         out->time.tv_sec = in->open_time.tv_sec;
140         out->time.tv_usec = in->open_time.tv_usec;
141         out->share_access = in->share_access;
142         out->access_mask = in->access_mask;
143         out->dev = (SMB_DEV_T)in->dev;
144         out->inode = (SMB_INO_T)in->ino;
145 }
146
147 /*
148  * Return the current share mode list for an open file.
149  * This uses similar (but simplified) logic to locking/locking.c
150  */
151
152 int smb_get_share_mode_entries(struct smbdb_ctx *db_ctx,
153                                 uint64_t dev,
154                                 uint64_t ino,
155                                 struct smb_share_mode_entry **pp_list,
156                                 unsigned char *p_delete_on_close)
157 {
158         TDB_DATA db_data;
159         struct smb_share_mode_entry *list = NULL;
160         int num_share_modes = 0;
161         struct locking_data *ld = NULL; /* internal samba db state. */
162         share_mode_entry *shares = NULL;
163         size_t i;
164         int list_num;
165
166         *pp_list = NULL;
167         *p_delete_on_close = 0;
168
169         db_data = tdb_fetch(db_ctx->smb_tdb, get_locking_key(dev, ino));
170         if (!db_data.dptr) {
171                 return 0;
172         }
173
174         ld = (struct locking_data *)db_data.dptr;
175         num_share_modes = ld->u.s.num_share_mode_entries;
176
177         if (!num_share_modes) {
178                 free(db_data.dptr);
179                 return 0;
180         }
181
182         list = (struct smb_share_mode_entry *)malloc(sizeof(struct smb_share_mode_entry)*num_share_modes);
183         if (!list) {
184                 free(db_data.dptr);
185                 return -1;
186         }
187
188         memset(list, '\0', num_share_modes * sizeof(struct smb_share_mode_entry));
189
190         shares = (share_mode_entry *)(db_data.dptr + sizeof(share_mode_entry));
191
192         list_num = 0;
193         for (i = 0; i < num_share_modes; i++) {
194                 share_mode_entry *share = &shares[i];
195                 struct smb_share_mode_entry *sme = &list[list_num];
196                 pid_t pid = share->pid;
197
198                 /* Check this process really exists. */
199                 if (kill(pid, 0) == -1 && (errno == ESRCH)) {
200                         continue; /* No longer exists. */
201                 }
202
203                 /* Copy into the external list. */
204                 sme->dev = (uint64_t)share->dev;
205                 sme->ino = (uint64_t)share->inode;
206                 sme->share_access = (uint32_t)share->share_access;
207                 sme->access_mask = (uint32_t)share->access_mask;
208                 sme->open_time.tv_sec = share->time.tv_sec;
209                 sme->open_time.tv_usec = share->time.tv_usec;
210                 sme->file_id = (uint32_t)share->share_file_id;
211                 sme->pid = share->pid;
212                 list_num++;
213         }
214
215         if (list_num == 0) {
216                 free(db_data.dptr);
217                 free(list);
218                 return 0;
219         }
220
221         *p_delete_on_close = ld->u.s.delete_on_close;
222         *pp_list = list;
223         free(db_data.dptr);
224         return list_num;
225 }
226
227 /* 
228  * Create an entry in the Samba share mode db.
229  */
230
231 int smb_create_share_mode_entry(struct smbdb_ctx *db_ctx,
232                                 uint64_t dev,
233                                 uint64_t ino,
234                                 const struct smb_share_mode_entry *new_entry,
235                                 const char *filename) /* Must be abolute utf8 path. */
236 {
237         TDB_DATA db_data;
238         TDB_DATA locking_key =  get_locking_key(dev, ino);
239         int orig_num_share_modes = 0;
240         struct locking_data *ld = NULL; /* internal samba db state. */
241         share_mode_entry *shares = NULL;
242         char *new_data_p = NULL;
243         size_t new_data_size = 0;
244
245         db_data = tdb_fetch(db_ctx->smb_tdb, locking_key);
246         if (!db_data.dptr) {
247                 /* We must create the entry. */
248                 db_data.dptr = malloc((2*sizeof(share_mode_entry)) + strlen(filename) + 1);
249                 if (!db_data.dptr) {
250                         return -1;
251                 }
252                 ld = (struct locking_data *)db_data.dptr;
253                 ld->u.s.num_share_mode_entries = 1;
254                 ld->u.s.delete_on_close = 0;
255                 shares = (share_mode_entry *)(db_data.dptr + sizeof(share_mode_entry));
256                 create_share_mode_entry(shares, new_entry);
257                 memcpy(db_data.dptr + 2*sizeof(share_mode_entry),
258                         filename,
259                         strlen(filename) + 1);
260
261                 db_data.dsize = 2*sizeof(share_mode_entry) + strlen(filename) + 1;
262                 if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_INSERT) == -1) {
263                         free(db_data.dptr);
264                         return -1;
265                 }
266                 free(db_data.dptr);
267                 return 0;
268         }
269
270         /* Entry exists, we must add a new entry. */
271         new_data_p = malloc(db_data.dsize + sizeof(share_mode_entry));
272         if (!new_data_p) {
273                 free(db_data.dptr);
274                 return -1;
275         }
276
277         ld = (struct locking_data *)db_data.dptr;
278         orig_num_share_modes = ld->u.s.num_share_mode_entries;
279
280         /* Copy the original data. */
281         memcpy(new_data_p, db_data.dptr, (orig_num_share_modes+1)*sizeof(share_mode_entry));
282
283         /* Add in the new share mode */
284         shares = (share_mode_entry *)(new_data_p +
285                         ((orig_num_share_modes+1)*sizeof(share_mode_entry)));
286
287         create_share_mode_entry(shares, new_entry);
288
289         ld = (struct locking_data *)new_data_p;
290         ld->u.s.num_share_mode_entries++;
291
292         /* Append the original filename */
293         memcpy(new_data_p + ((ld->u.s.num_share_mode_entries+1)*sizeof(share_mode_entry)),
294                 db_data.dptr + ((orig_num_share_modes+1)*sizeof(share_mode_entry)),
295                 db_data.dsize - ((orig_num_share_modes+1) * sizeof(share_mode_entry)));
296
297         new_data_size = db_data.dsize + sizeof(share_mode_entry);
298
299         free(db_data.dptr);
300
301         db_data.dptr = new_data_p;
302         db_data.dsize = new_data_size;
303
304         if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_REPLACE) == -1) {
305                 free(db_data.dptr);
306                 return -1;
307         }
308         free(db_data.dptr);
309         return 0;
310 }
311
312 int smb_delete_share_mode_entry(struct smbdb_ctx *db_ctx,
313                                 uint64_t dev,
314                                 uint64_t ino,
315                                 const struct smb_share_mode_entry *del_entry)
316 {
317         TDB_DATA db_data;
318         TDB_DATA locking_key =  get_locking_key(dev, ino);
319         int orig_num_share_modes = 0;
320         struct locking_data *ld = NULL; /* internal samba db state. */
321         share_mode_entry *shares = NULL;
322         char *new_data_p = NULL;
323         size_t filename_size = 0;
324         size_t i, num_share_modes;
325         const char *fname_ptr = NULL;
326
327         db_data = tdb_fetch(db_ctx->smb_tdb, locking_key);
328         if (!db_data.dptr) {
329                 return -1; /* Error - missing entry ! */
330         }
331
332         ld = (struct locking_data *)db_data.dptr;
333         orig_num_share_modes = ld->u.s.num_share_mode_entries;
334         shares = (share_mode_entry *)(db_data.dptr + sizeof(share_mode_entry));
335
336         if (orig_num_share_modes == 1) {
337                 /* Only one entry - better be ours... */
338                 if (!share_mode_entry_equal(del_entry, shares)) {
339                         /* Error ! We can't delete someone else's entry ! */
340                         free(db_data.dptr);
341                         return -1;
342                 }
343                 /* It's ours - just remove the entire record. */
344                 free(db_data.dptr);
345                 return tdb_delete(db_ctx->smb_tdb, locking_key);
346         }
347
348         /* More than one - allocate a new record minus the one we'll delete. */
349         new_data_p = malloc(db_data.dsize - sizeof(share_mode_entry));
350         if (!new_data_p) {
351                 free(db_data.dptr);
352                 return -1;
353         }
354
355         /* Copy the header. */
356         memcpy(new_data_p, db_data.dptr, sizeof(share_mode_entry));
357
358         num_share_modes = 0;
359         for (i = 0; i < orig_num_share_modes; i++) {
360                 share_mode_entry *share = &shares[i];
361                 pid_t pid = share->pid;
362
363                 /* Check this process really exists. */
364                 if (kill(pid, 0) == -1 && (errno == ESRCH)) {
365                         continue; /* No longer exists. */
366                 }
367
368                 if (share_mode_entry_equal(del_entry, share)) {
369                         continue; /* This is our delete taget. */
370                 }
371
372                 memcpy(new_data_p + ((num_share_modes+1)*sizeof(share_mode_entry)),
373                         share, sizeof(share_mode_entry) );
374
375                 num_share_modes++;
376         }
377
378         if (num_share_modes == 0) {
379                 /* None left after pruning. Delete record. */
380                 free(db_data.dptr);
381                 free(new_data_p);
382                 return tdb_delete(db_ctx->smb_tdb, locking_key);
383         }
384
385         /* Copy the terminating filename. */
386         fname_ptr = db_data.dptr + ((orig_num_share_modes+1) * sizeof(share_mode_entry));
387         filename_size = db_data.dsize - (fname_ptr - db_data.dptr);
388
389         memcpy(new_data_p + ((num_share_modes+1)*sizeof(share_mode_entry)),
390                 fname_ptr,
391                 filename_size);
392
393         free(db_data.dptr);
394
395         db_data.dptr = new_data_p;
396
397         /* Re-save smaller record. */
398         ld = (struct locking_data *)db_data.dptr;
399         ld->u.s.num_share_mode_entries = num_share_modes;
400
401         db_data.dsize = ((num_share_modes+1)*sizeof(share_mode_entry)) + filename_size;
402
403         if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_REPLACE) == -1) {
404                 free(db_data.dptr);
405                 return -1;
406         }
407         free(db_data.dptr);
408         return 0;
409 }
410
411 int smb_change_share_mode_entry(struct smbdb_ctx *db_ctx,
412                                 uint64_t dev,
413                                 uint64_t ino,
414                                 const struct smb_share_mode_entry *set_entry,
415                                 const struct smb_share_mode_entry *new_entry)
416 {
417         TDB_DATA db_data;
418         TDB_DATA locking_key =  get_locking_key(dev, ino);
419         int num_share_modes = 0;
420         struct locking_data *ld = NULL; /* internal samba db state. */
421         share_mode_entry *shares = NULL;
422         size_t i;
423         int found_entry = 0;
424
425         db_data = tdb_fetch(db_ctx->smb_tdb, locking_key);
426         if (!db_data.dptr) {
427                 return -1; /* Error - missing entry ! */
428         }
429
430         ld = (struct locking_data *)db_data.dptr;
431         num_share_modes = ld->u.s.num_share_mode_entries;
432         shares = (share_mode_entry *)(db_data.dptr + sizeof(share_mode_entry));
433
434         for (i = 0; i < num_share_modes; i++) {
435                 share_mode_entry *share = &shares[i];
436                 pid_t pid = share->pid;
437
438                 /* Check this process really exists. */
439                 if (kill(pid, 0) == -1 && (errno == ESRCH)) {
440                         continue; /* No longer exists. */
441                 }
442
443                 if (share_mode_entry_equal(set_entry, share)) {
444                         create_share_mode_entry(share, new_entry);
445                         found_entry = 1;
446                         break;
447                 }
448         }
449
450         if (!found_entry) {
451                 free(db_data.dptr);
452                 return -1;
453         }
454
455         /* Save modified data. */
456         if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_REPLACE) == -1) {
457                 free(db_data.dptr);
458                 return -1;
459         }
460         free(db_data.dptr);
461         return 0;
462 }