r10656: BIG merge from trunk. Features not copied over
[kai/samba.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 /* key and data records in the tdb locking database */
59 struct locking_key {
60         SMB_DEV_T dev;
61         SMB_INO_T inode;
62 };
63
64 int smb_share_mode_db_close(struct smbdb_ctx *db_ctx)
65 {
66         int ret = tdb_close(db_ctx->smb_tdb);
67         free(db_ctx);
68         return ret;
69 }
70
71 static TDB_DATA get_locking_key(uint64_t dev, uint64_t ino)
72 {
73         static struct locking_key lk;
74         TDB_DATA ld;
75
76         memset(&lk, '\0', sizeof(struct locking_key));
77         lk.dev = (SMB_DEV_T)dev;
78         lk.inode = (SMB_INO_T)ino;
79         ld.dptr = (char *)&lk;
80         ld.dsize = sizeof(lk);
81         return ld;
82 }
83
84 /*
85  * lock/unlock entry in sharemode database.
86  */
87
88 int smb_lock_share_mode_entry(struct smbdb_ctx *db_ctx,
89                                 uint64_t dev,
90                                 uint64_t ino)
91 {
92         return tdb_chainlock(db_ctx->smb_tdb, get_locking_key(dev, ino));
93 }
94                                                                                                                                   
95 int smb_unlock_share_mode_entry(struct smbdb_ctx *db_ctx,
96                                 uint64_t dev,
97                                 uint64_t ino)
98 {
99         return tdb_chainunlock(db_ctx->smb_tdb, get_locking_key(dev, ino));
100 }
101
102 /* Internal structure of Samba share mode db. */
103 /* FIXME ! This should be moved into a Samba include file. */
104
105 struct locking_data {
106         union {
107                 struct {
108                         int num_share_mode_entries;
109                         BOOL delete_on_close;
110                 } s;
111                 struct share_mode_entry dummy; /* Needed for alignment. */
112         } u;
113         /* the following two entries are implicit
114            struct share_mode_entry modes[num_share_mode_entries];
115            char file_name[];
116         */
117 };
118
119 /*
120  * Check if an external smb_share_mode_entry and an internal share_mode entry match.
121  */
122
123 static int share_mode_entry_equal(const struct smb_share_mode_entry *e_entry, const struct share_mode_entry *entry)
124 {
125         return (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, const struct smb_share_mode_entry *in)
140 {
141         memset(out, '\0', sizeof(struct share_mode_entry));
142
143         out->pid = in->pid;
144         out->share_file_id = (unsigned long)in->file_id;
145         out->time.tv_sec = in->open_time.tv_sec;
146         out->time.tv_usec = in->open_time.tv_usec;
147         out->share_access = in->share_access;
148         out->access_mask = in->access_mask;
149         out->dev = (SMB_DEV_T)in->dev;
150         out->inode = (SMB_INO_T)in->ino;
151 }
152
153 /*
154  * Return the current share mode list for an open file.
155  * This uses similar (but simplified) logic to locking/locking.c
156  */
157
158 int smb_get_share_mode_entries(struct smbdb_ctx *db_ctx,
159                                 uint64_t dev,
160                                 uint64_t ino,
161                                 struct smb_share_mode_entry **pp_list,
162                                 unsigned char *p_delete_on_close)
163 {
164         TDB_DATA db_data;
165         struct smb_share_mode_entry *list = NULL;
166         int num_share_modes = 0;
167         struct locking_data *ld = NULL; /* internal samba db state. */
168         struct share_mode_entry *shares = NULL;
169         size_t i;
170         int list_num;
171
172         *pp_list = NULL;
173         *p_delete_on_close = 0;
174
175         db_data = tdb_fetch(db_ctx->smb_tdb, get_locking_key(dev, ino));
176         if (!db_data.dptr) {
177                 return 0;
178         }
179
180         ld = (struct locking_data *)db_data.dptr;
181         num_share_modes = ld->u.s.num_share_mode_entries;
182
183         if (!num_share_modes) {
184                 free(db_data.dptr);
185                 return 0;
186         }
187
188         list = (struct smb_share_mode_entry *)malloc(sizeof(struct smb_share_mode_entry)*num_share_modes);
189         if (!list) {
190                 free(db_data.dptr);
191                 return -1;
192         }
193
194         memset(list, '\0', num_share_modes * sizeof(struct smb_share_mode_entry));
195
196         shares = (struct share_mode_entry *)(db_data.dptr + sizeof(struct share_mode_entry));
197
198         list_num = 0;
199         for (i = 0; i < num_share_modes; i++) {
200                 struct share_mode_entry *share = &shares[i];
201                 struct smb_share_mode_entry *sme = &list[list_num];
202                 struct process_id pid = share->pid;
203
204                 /* Check this process really exists. */
205                 if (kill(procid_to_pid(&pid), 0) == -1 && (errno == ESRCH)) {
206                         continue; /* No longer exists. */
207                 }
208
209                 /* Ignore deferred open entries. */
210                 if (share->op_type == DEFERRED_OPEN_ENTRY) {
211                         continue;
212                 }
213
214                 /* Copy into the external list. */
215                 sme->dev = (uint64_t)share->dev;
216                 sme->ino = (uint64_t)share->inode;
217                 sme->share_access = (uint32_t)share->share_access;
218                 sme->access_mask = (uint32_t)share->access_mask;
219                 sme->open_time.tv_sec = share->time.tv_sec;
220                 sme->open_time.tv_usec = share->time.tv_usec;
221                 sme->file_id = (uint32_t)share->share_file_id;
222                 sme->pid = share->pid;
223                 list_num++;
224         }
225
226         if (list_num == 0) {
227                 free(db_data.dptr);
228                 free(list);
229                 return 0;
230         }
231
232         *p_delete_on_close = ld->u.s.delete_on_close;
233         *pp_list = list;
234         free(db_data.dptr);
235         return list_num;
236 }
237
238 /* 
239  * Create an entry in the Samba share mode db.
240  */
241
242 int smb_create_share_mode_entry(struct smbdb_ctx *db_ctx,
243                                 uint64_t dev,
244                                 uint64_t ino,
245                                 const struct smb_share_mode_entry *new_entry,
246                                 const char *filename) /* Must be abolute utf8 path. */
247 {
248         TDB_DATA db_data;
249         TDB_DATA locking_key =  get_locking_key(dev, ino);
250         int orig_num_share_modes = 0;
251         struct locking_data *ld = NULL; /* internal samba db state. */
252         struct share_mode_entry *shares = NULL;
253         char *new_data_p = NULL;
254         size_t new_data_size = 0;
255
256         db_data = tdb_fetch(db_ctx->smb_tdb, locking_key);
257         if (!db_data.dptr) {
258                 /* We must create the entry. */
259                 db_data.dptr = malloc((2*sizeof(struct share_mode_entry)) + strlen(filename) + 1);
260                 if (!db_data.dptr) {
261                         return -1;
262                 }
263                 ld = (struct locking_data *)db_data.dptr;
264                 ld->u.s.num_share_mode_entries = 1;
265                 ld->u.s.delete_on_close = 0;
266                 shares = (struct share_mode_entry *)(db_data.dptr + sizeof(struct share_mode_entry));
267                 create_share_mode_entry(shares, new_entry);
268                 memcpy(db_data.dptr + 2*sizeof(struct share_mode_entry),
269                         filename,
270                         strlen(filename) + 1);
271
272                 db_data.dsize = 2*sizeof(struct share_mode_entry) + strlen(filename) + 1;
273                 if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_INSERT) == -1) {
274                         free(db_data.dptr);
275                         return -1;
276                 }
277                 free(db_data.dptr);
278                 return 0;
279         }
280
281         /* Entry exists, we must add a new entry. */
282         new_data_p = malloc(db_data.dsize + sizeof(struct share_mode_entry));
283         if (!new_data_p) {
284                 free(db_data.dptr);
285                 return -1;
286         }
287
288         ld = (struct locking_data *)db_data.dptr;
289         orig_num_share_modes = ld->u.s.num_share_mode_entries;
290
291         /* Copy the original data. */
292         memcpy(new_data_p, db_data.dptr, (orig_num_share_modes+1)*sizeof(struct share_mode_entry));
293
294         /* Add in the new share mode */
295         shares = (struct share_mode_entry *)(new_data_p +
296                         ((orig_num_share_modes+1)*sizeof(struct share_mode_entry)));
297
298         create_share_mode_entry(shares, new_entry);
299
300         ld = (struct locking_data *)new_data_p;
301         ld->u.s.num_share_mode_entries++;
302
303         /* Append the original filename */
304         memcpy(new_data_p + ((ld->u.s.num_share_mode_entries+1)*sizeof(struct share_mode_entry)),
305                 db_data.dptr + ((orig_num_share_modes+1)*sizeof(struct share_mode_entry)),
306                 db_data.dsize - ((orig_num_share_modes+1) * sizeof(struct share_mode_entry)));
307
308         new_data_size = db_data.dsize + sizeof(struct share_mode_entry);
309
310         free(db_data.dptr);
311
312         db_data.dptr = new_data_p;
313         db_data.dsize = new_data_size;
314
315         if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_REPLACE) == -1) {
316                 free(db_data.dptr);
317                 return -1;
318         }
319         free(db_data.dptr);
320         return 0;
321 }
322
323 int smb_delete_share_mode_entry(struct smbdb_ctx *db_ctx,
324                                 uint64_t dev,
325                                 uint64_t ino,
326                                 const struct smb_share_mode_entry *del_entry)
327 {
328         TDB_DATA db_data;
329         TDB_DATA locking_key =  get_locking_key(dev, ino);
330         int orig_num_share_modes = 0;
331         struct locking_data *ld = NULL; /* internal samba db state. */
332         struct share_mode_entry *shares = NULL;
333         char *new_data_p = NULL;
334         size_t filename_size = 0;
335         size_t i, num_share_modes;
336         const char *fname_ptr = NULL;
337
338         db_data = tdb_fetch(db_ctx->smb_tdb, locking_key);
339         if (!db_data.dptr) {
340                 return -1; /* Error - missing entry ! */
341         }
342
343         ld = (struct locking_data *)db_data.dptr;
344         orig_num_share_modes = ld->u.s.num_share_mode_entries;
345         shares = (struct share_mode_entry *)(db_data.dptr + sizeof(struct share_mode_entry));
346
347         if (orig_num_share_modes == 1) {
348                 /* Only one entry - better be ours... */
349                 if (!share_mode_entry_equal(del_entry, shares)) {
350                         /* Error ! We can't delete someone else's entry ! */
351                         free(db_data.dptr);
352                         return -1;
353                 }
354                 /* It's ours - just remove the entire record. */
355                 free(db_data.dptr);
356                 return tdb_delete(db_ctx->smb_tdb, locking_key);
357         }
358
359         /* More than one - allocate a new record minus the one we'll delete. */
360         new_data_p = malloc(db_data.dsize - sizeof(struct share_mode_entry));
361         if (!new_data_p) {
362                 free(db_data.dptr);
363                 return -1;
364         }
365
366         /* Copy the header. */
367         memcpy(new_data_p, db_data.dptr, sizeof(struct share_mode_entry));
368
369         num_share_modes = 0;
370         for (i = 0; i < orig_num_share_modes; i++) {
371                 struct share_mode_entry *share = &shares[i];
372                 struct process_id pid = share->pid;
373
374                 /* Check this process really exists. */
375                 if (kill(procid_to_pid(&pid), 0) == -1 && (errno == ESRCH)) {
376                         continue; /* No longer exists. */
377                 }
378
379                 if (share_mode_entry_equal(del_entry, share)) {
380                         continue; /* This is our delete taget. */
381                 }
382
383                 memcpy(new_data_p + ((num_share_modes+1)*sizeof(struct share_mode_entry)),
384                         share, sizeof(struct share_mode_entry) );
385
386                 num_share_modes++;
387         }
388
389         if (num_share_modes == 0) {
390                 /* None left after pruning. Delete record. */
391                 free(db_data.dptr);
392                 free(new_data_p);
393                 return tdb_delete(db_ctx->smb_tdb, locking_key);
394         }
395
396         /* Copy the terminating filename. */
397         fname_ptr = db_data.dptr + ((orig_num_share_modes+1) * sizeof(struct share_mode_entry));
398         filename_size = db_data.dsize - (fname_ptr - db_data.dptr);
399
400         memcpy(new_data_p + ((num_share_modes+1)*sizeof(struct share_mode_entry)),
401                 fname_ptr,
402                 filename_size);
403
404         free(db_data.dptr);
405
406         db_data.dptr = new_data_p;
407
408         /* Re-save smaller record. */
409         ld = (struct locking_data *)db_data.dptr;
410         ld->u.s.num_share_mode_entries = num_share_modes;
411
412         db_data.dsize = ((num_share_modes+1)*sizeof(struct share_mode_entry)) + filename_size;
413
414         if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_REPLACE) == -1) {
415                 free(db_data.dptr);
416                 return -1;
417         }
418         free(db_data.dptr);
419         return 0;
420 }
421
422 int smb_change_share_mode_entry(struct smbdb_ctx *db_ctx,
423                                 uint64_t dev,
424                                 uint64_t ino,
425                                 const struct smb_share_mode_entry *set_entry,
426                                 const struct smb_share_mode_entry *new_entry)
427 {
428         TDB_DATA db_data;
429         TDB_DATA locking_key =  get_locking_key(dev, ino);
430         int num_share_modes = 0;
431         struct locking_data *ld = NULL; /* internal samba db state. */
432         struct share_mode_entry *shares = NULL;
433         size_t i;
434         int found_entry = 0;
435
436         db_data = tdb_fetch(db_ctx->smb_tdb, locking_key);
437         if (!db_data.dptr) {
438                 return -1; /* Error - missing entry ! */
439         }
440
441         ld = (struct locking_data *)db_data.dptr;
442         num_share_modes = ld->u.s.num_share_mode_entries;
443         shares = (struct share_mode_entry *)(db_data.dptr + sizeof(struct share_mode_entry));
444
445         for (i = 0; i < num_share_modes; i++) {
446                 struct share_mode_entry *share = &shares[i];
447                 struct process_id pid = share->pid;
448
449                 /* Check this process really exists. */
450                 if (kill(procid_to_pid(&pid), 0) == -1 && (errno == ESRCH)) {
451                         continue; /* No longer exists. */
452                 }
453
454                 if (share_mode_entry_equal(set_entry, share)) {
455                         create_share_mode_entry(share, new_entry);
456                         found_entry = 1;
457                         break;
458                 }
459         }
460
461         if (!found_entry) {
462                 free(db_data.dptr);
463                 return -1;
464         }
465
466         /* Save modified data. */
467         if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_REPLACE) == -1) {
468                 free(db_data.dptr);
469                 return -1;
470         }
471         free(db_data.dptr);
472         return 0;
473 }