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