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