~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source3/lib/dbwrap_file.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}