7e5f9596751411c6466babc25acb5744435f36cd
[ira/wip.git] / source3 / lib / dbwrap_file.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Database interface using a file per record
4    Copyright (C) Volker Lendecke 2005
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /*
22  * Be aware that this is just sample code that has not seen too much testing
23  */
24
25 #include "includes.h"
26
27 struct db_file_ctx {
28         const char *dirname;
29
30         /* We only support one locked record at a time -- everything else
31          * would lead to a potential deadlock anyway! */
32         struct db_record *locked_record;
33 };
34
35 struct db_locked_file {
36         int fd;
37         uint8 hash;
38         const char *name;
39         const char *path;
40         struct db_file_ctx *parent;
41 };
42
43 /* Copy from statcache.c... */
44
45 static uint32 fsh(const uint8 *p, int len)
46 {
47         uint32 n = 0;
48         int i;
49         for (i=0; i<len; i++) {
50                 n = ((n << 5) + n) ^ (uint32)(p[i]);
51         }
52         return n;
53 }
54
55 static int db_locked_file_destr(struct db_locked_file *data)
56 {
57         if (data->parent != NULL) {
58                 data->parent->locked_record = NULL;
59         }
60
61         if (close(data->fd) != 0) {
62                 DEBUG(3, ("close failed: %s\n", strerror(errno)));
63                 return -1;
64         }
65
66         return 0;
67 }
68
69 static NTSTATUS db_file_store(struct db_record *rec, TDB_DATA data, int flag);
70 static NTSTATUS db_file_delete(struct db_record *rec);
71
72 static struct db_record *db_file_fetch_locked(struct db_context *db,
73                                               TALLOC_CTX *mem_ctx,
74                                               TDB_DATA key)
75 {
76         struct db_file_ctx *ctx = talloc_get_type_abort(db->private_data,
77                                                         struct db_file_ctx);
78         struct db_record *result;
79         struct db_locked_file *file;
80         struct flock fl;
81         SMB_STRUCT_STAT statbuf;
82         ssize_t nread;
83         int ret;
84
85         SMB_ASSERT(ctx->locked_record == NULL);
86
87  again:
88         if (!(result = TALLOC_P(mem_ctx, struct db_record))) {
89                 DEBUG(0, ("talloc failed\n"));
90                 return NULL;
91         }
92
93         if (!(file = TALLOC_P(result, struct db_locked_file))) {
94                 DEBUG(0, ("talloc failed\n"));
95                 TALLOC_FREE(result);
96                 return NULL;
97         }
98
99         result->private_data = file;
100         result->store = db_file_store;
101         result->delete_rec = db_file_delete;
102
103         result->key.dsize = key.dsize;
104         result->key.dptr = (uint8 *)talloc_memdup(result, key.dptr, key.dsize);
105         if (result->key.dptr == NULL) {
106                 DEBUG(0, ("talloc failed\n"));
107                 TALLOC_FREE(result);
108                 return NULL;
109         }
110
111         /* Cut to 8 bits */
112         file->hash = fsh(key.dptr, key.dsize);
113         file->name = hex_encode(file, (unsigned char *)key.dptr, key.dsize);
114         if (file->name == NULL) {
115                 DEBUG(0, ("hex_encode failed\n"));
116                 TALLOC_FREE(result);
117                 return NULL;
118         }
119
120         file->path = talloc_asprintf(file, "%s/%2.2X/%s", ctx->dirname,
121                                      file->hash, file->name);
122         if (file->path == NULL) {
123                 DEBUG(0, ("talloc_asprintf failed\n"));
124                 TALLOC_FREE(result);
125                 return NULL;
126         }
127
128         become_root();
129         file->fd = open(file->path, O_RDWR|O_CREAT, 0644);
130         unbecome_root();
131
132         if (file->fd < 0) {
133                 DEBUG(3, ("Could not open/create %s: %s\n",
134                           file->path, strerror(errno)));
135                 TALLOC_FREE(result);
136                 return NULL;
137         }
138
139         talloc_set_destructor(file, db_locked_file_destr);
140
141         fl.l_type = F_WRLCK;
142         fl.l_whence = SEEK_SET;
143         fl.l_start = 0;
144         fl.l_len = 1;
145         fl.l_pid = 0;
146
147         do {
148                 ret = fcntl(file->fd, F_SETLKW, &fl);
149         } while ((ret == -1) && (errno == EINTR));
150
151         if (ret == -1) {
152                 DEBUG(3, ("Could not get lock on %s: %s\n",
153                           file->path, strerror(errno)));
154                 TALLOC_FREE(result);
155                 return NULL;
156         }
157
158         if (sys_fstat(file->fd, &statbuf) != 0) {
159                 DEBUG(3, ("Could not fstat %s: %s\n",
160                           file->path, strerror(errno)));
161                 TALLOC_FREE(result);
162                 return NULL;
163         }
164
165         if (statbuf.st_nlink == 0) {
166                 /* Someone has deleted it under the lock, retry */
167                 TALLOC_FREE(result);
168                 goto again;
169         }
170
171         result->value.dsize = 0;
172         result->value.dptr = NULL;
173
174         if (statbuf.st_size != 0) {
175                 result->value.dsize = statbuf.st_size;
176                 result->value.dptr = TALLOC_ARRAY(result, uint8,
177                                                   statbuf.st_size);
178                 if (result->value.dptr == NULL) {
179                         DEBUG(1, ("talloc failed\n"));
180                         TALLOC_FREE(result);
181                         return NULL;
182                 }
183
184                 nread = read_data(file->fd, (char *)result->value.dptr,
185                                   result->value.dsize);
186                 if (nread != result->value.dsize) {
187                         DEBUG(3, ("read_data failed: %s\n", strerror(errno)));
188                         TALLOC_FREE(result);
189                         return NULL;
190                 }
191         }
192
193         ctx->locked_record = result;
194         file->parent = (struct db_file_ctx *)talloc_reference(file, ctx);
195
196         return result;
197 }
198
199 static NTSTATUS db_file_store_root(int fd, TDB_DATA data)
200 {
201         if (sys_lseek(fd, 0, SEEK_SET) != 0) {
202                 DEBUG(0, ("sys_lseek failed: %s\n", strerror(errno)));
203                 return map_nt_error_from_unix(errno);
204         }
205
206         if (write_data(fd, (char *)data.dptr, data.dsize) != data.dsize) {
207                 DEBUG(3, ("write_data failed: %s\n", strerror(errno)));
208                 return map_nt_error_from_unix(errno);
209         }
210
211         if (sys_ftruncate(fd, data.dsize) != 0) {
212                 DEBUG(3, ("sys_ftruncate failed: %s\n", strerror(errno)));
213                 return map_nt_error_from_unix(errno);
214         }
215
216         return NT_STATUS_OK;
217 }
218
219 static NTSTATUS db_file_store(struct db_record *rec, TDB_DATA data, int flag)
220 {
221         struct db_locked_file *file =
222                 talloc_get_type_abort(rec->private_data,
223                                       struct db_locked_file);
224         NTSTATUS status;
225
226         become_root();
227         status = db_file_store_root(file->fd, data);
228         unbecome_root();
229
230         return status;
231 }
232
233 static NTSTATUS db_file_delete(struct db_record *rec)
234 {
235         struct db_locked_file *file =
236                 talloc_get_type_abort(rec->private_data,
237                                       struct db_locked_file);
238         int res;
239
240         become_root();
241         res = unlink(file->path);
242         unbecome_root();
243
244         if (res == -1) {
245                 DEBUG(3, ("unlink(%s) failed: %s\n", file->path,
246                           strerror(errno)));
247                 return map_nt_error_from_unix(errno);
248         }
249
250         return NT_STATUS_OK;
251 }
252
253 static int db_file_traverse(struct db_context *db,
254                             int (*fn)(struct db_record *rec,
255                                       void *private_data),
256                             void *private_data)
257 {
258         struct db_file_ctx *ctx = talloc_get_type_abort(db->private_data,
259                                                         struct db_file_ctx);
260         TALLOC_CTX *mem_ctx = talloc_init("traversal %s\n", ctx->dirname);
261         
262         int i;
263         int count = 0;
264
265         for (i=0; i<256; i++) {
266                 const char *dirname = talloc_asprintf(mem_ctx, "%s/%2.2X",
267                                                       ctx->dirname, i);
268                 DIR *dir;
269                 struct dirent *dirent;
270
271                 if (dirname == NULL) {
272                         DEBUG(0, ("talloc failed\n"));
273                         TALLOC_FREE(mem_ctx);
274                         return -1;
275                 }
276
277                 dir = opendir(dirname);
278                 if (dir == NULL) {
279                         DEBUG(3, ("Could not open dir %s: %s\n", dirname,
280                                   strerror(errno)));
281                         TALLOC_FREE(mem_ctx);
282                         return -1;
283                 }
284
285                 while ((dirent = readdir(dir)) != NULL) {
286                         DATA_BLOB keyblob;
287                         TDB_DATA key;
288                         struct db_record *rec;
289
290                         if ((dirent->d_name[0] == '.') &&
291                             ((dirent->d_name[1] == '\0') ||
292                              ((dirent->d_name[1] == '.') &&
293                               (dirent->d_name[2] == '\0')))) {
294                                 continue;
295                         }
296
297                         keyblob = strhex_to_data_blob(mem_ctx, dirent->d_name);
298                         if (keyblob.data == NULL) {
299                                 DEBUG(5, ("strhex_to_data_blob failed\n"));
300                                 continue;
301                         }
302
303                         key.dptr = keyblob.data;
304                         key.dsize = keyblob.length;
305
306                         if ((ctx->locked_record != NULL) &&
307                             (key.dsize == ctx->locked_record->key.dsize) &&
308                             (memcmp(key.dptr, ctx->locked_record->key.dptr,
309                                     key.dsize) == 0)) {
310                                 count += 1;
311                                 if (fn(ctx->locked_record,
312                                        private_data) != 0) {
313                                         TALLOC_FREE(mem_ctx);
314                                         closedir(dir);
315                                         return count;
316                                 }
317                         }
318
319                         rec = db_file_fetch_locked(db, mem_ctx, key);
320                         if (rec == NULL) {
321                                 /* Someone might have deleted it */
322                                 continue;
323                         }
324
325                         if (rec->value.dptr == NULL) {
326                                 TALLOC_FREE(rec);
327                                 continue;
328                         }
329
330                         count += 1;
331
332                         if (fn(rec, private_data) != 0) {
333                                 TALLOC_FREE(mem_ctx);
334                                 closedir(dir);
335                                 return count;
336                         }
337                         TALLOC_FREE(rec);
338                 }
339
340                 closedir(dir);
341         }
342
343         TALLOC_FREE(mem_ctx);
344         return count;
345 }
346
347 struct db_context *db_open_file(TALLOC_CTX *mem_ctx,
348                                 struct messaging_context *msg_ctx,
349                                 const char *name,
350                                 int hash_size, int tdb_flags,
351                                 int open_flags, mode_t mode)
352 {
353         struct db_context *result = NULL;
354         struct db_file_ctx *ctx;
355
356         if (!(result = TALLOC_ZERO_P(mem_ctx, struct db_context))) {
357                 DEBUG(0, ("talloc failed\n"));
358                 return NULL;
359         }
360
361         if (!(ctx = TALLOC_P(result, struct db_file_ctx))) {
362                 DEBUG(0, ("talloc failed\n"));
363                 TALLOC_FREE(result);
364                 return NULL;
365         }
366
367         result->private_data = ctx;
368         result->fetch_locked = db_file_fetch_locked;
369         result->traverse = db_file_traverse;
370         result->traverse_read = db_file_traverse;
371
372         ctx->locked_record = NULL;
373         if (!(ctx->dirname = talloc_strdup(ctx, name))) {
374                 DEBUG(0, ("talloc failed\n"));
375                 TALLOC_FREE(result);
376                 return NULL;
377         }
378
379         if (open_flags & O_CREAT) {
380                 int ret, i;
381
382                 mode |= (mode & S_IRUSR) ? S_IXUSR : 0;
383                 mode |= (mode & S_IRGRP) ? S_IXGRP : 0;
384                 mode |= (mode & S_IROTH) ? S_IXOTH : 0;
385
386                 ret = mkdir(name, mode);
387                 if ((ret != 0) && (errno != EEXIST)) {
388                         DEBUG(5, ("mkdir(%s,%o) failed: %s\n", name, mode,
389                                   strerror(errno)));
390                         TALLOC_FREE(result);
391                         return NULL;
392                 }
393
394                 for (i=0; i<256; i++) {
395                         char *path;
396                         path = talloc_asprintf(result, "%s/%2.2X", name, i);
397                         if (path == NULL) {
398                                 DEBUG(0, ("asprintf failed\n"));
399                                 TALLOC_FREE(result);
400                                 return NULL;
401                         }
402                         ret = mkdir(path, mode);
403                         if ((ret != 0) && (errno != EEXIST)) {
404                                 DEBUG(5, ("mkdir(%s,%o) failed: %s\n", path,
405                                           mode, strerror(errno)));
406                                 TALLOC_FREE(result);
407                                 return NULL;
408                         }
409                         TALLOC_FREE(path);
410                 }
411         }
412
413         return result;
414 }