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
22struct 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
30struct 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
40static 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
50static 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
64static NTSTATUS db_file_store(struct db_record *rec, TDB_DATA data, int flag);
65static NTSTATUS db_file_delete(struct db_record *rec);
66
67static 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
194static 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
214static 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
228static 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
248static 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
342struct 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}
411