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