1 /***************************************************************************
2  *                                                                         *
3  *   This program is free software; you can redistribute it and/or modify  *
4  *   it under the terms of the GNU General Public License as published by  *
5  *   the Free Software Foundation; either version 2 of the License, or     *
6  *   (at your option) any later version.                                   *
7  *                                                                         *
8  ***************************************************************************
9  * fs/directory.cc -- part of ezbounce
10  * (c) 2006-2008 Murat Deligonul
11  */
12 
13 #include "autoconf.h"
14 
15 #include <vector>
16 #include <cstring>
17 #include <cstdlib>
18 #include <cstdio>
19 #include <ctime>
20 #include <cerrno>
21 #include <unistd.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include "util/strings.h"
26 #include "util/generic.h"
27 #include "fs/directory.h"
28 #include "fs/error.h"
29 #include "io/utility.h"
30 #include "debug.h"
31 
32 using namespace util::strings;
33 
34 namespace fs {
35 
36 /**
37  * Database file version.  History:
38  *
39  * 	201	-	(current) added permissions and file group data.
40  * 			changed to three digit version numbers to handle minor changes.
41  * 	  2	-	old format, used until 1.99.11
42  */
43 const unsigned short  directory::DB_VERSION = 201;
44 
45 const char directory::DB_FILE_NAME[] = "ezbfiles.db";
46 
47 /**
48  * Construct directory object from a parent directory
49  * and entry structure.
50  * Directory names and paths are stored without a trailing '/'
51  *  unless they refer to a root directory.
52  */
directory(directory * p,file_entry * f)53 directory::directory(directory * p, file_entry * f)
54 	: _name(f->name()),
55 		_entry(f),
56 		fd(-1),
57 		batch_mode(false),
58 		last_mod(0), last_size(0),
59 		file_cache(CACHE_BUCKETS)
60 {
61 	assert(f->dir() == p);
62 	int plen = strlen(p->abs_path());
63 
64 	a_path = my_strdup3(p->abs_path(), "/", _name);
65 
66 	r_path = a_path + (plen - strlen(p->rel_path()));
67 	if (p->parent() == p) {
68 		++r_path;
69 	}
70 
71 	dbfile = my_strdup3(a_path, "/", DB_FILE_NAME);
72 
73 	parent()->inc_count();
74 	parent()->add_child(this);
75 
76 	_entry->inc_count();
77 
78 	assert(r_path[0] == '/');
79 	assert(r_path[strlen(r_path)-1] != '/');
80 	assert(_entry->dir() == parent());
81 }
82 
83 
84 /**
85  * Construct directory object for virtual root directory.
86  * Takes in parameter of absolute path this VFS resides in.
87  */
directory(const char * p,const char * owner,const char * group)88 directory::directory(const char * p, const char * owner, const char * group)
89 	: fd(-1),
90 		batch_mode(false),
91 		last_mod(0), last_size(0),
92 		file_cache(CACHE_BUCKETS)
93 {
94 	int plen = strlen(p);
95 	a_path = my_strdup(p);
96 
97 	/* strip any trailing '/' characters, unless we are actually
98 	   setting up in '/' -- ( why the hell would someone do that ???? )*/
99 	while (a_path[plen-1] == '/' && plen > 1) {
100 		a_path[--plen] = 0;
101 	}
102 
103 	dbfile = my_strdup3(a_path, "/", DB_FILE_NAME);
104 	_entry = new file_entry(this, "", owner, group);
105 	_name = _entry->name();
106 	r_path = "/";
107 	_entry->set_mode(0755);
108 	_entry->set_flags(FE_DIRECTORY);
109 	_entry->inc_count();
110 	cache_insert(_entry);
111 }
112 
~directory()113 directory::~directory()
114 {
115 	DEBUG("%s: called for '%s'\n", __PRETTY_FUNCTION__, rel_path());
116 
117 #ifdef __DEBUG__
118 	// NOTE: intense debug output here
119 	std::string obj;
120 	util::print_container(objects(), obj);
121 	DEBUG("%s: remaining dirs: %s\n", __PRETTY_FUNCTION__, obj.c_str());
122 	util::print_container(file_entry::objects(), obj);
123 	DEBUG("%s: remaining file entries: %s\n", __PRETTY_FUNCTION__, obj.c_str());
124 #endif
125 
126 	cache_prune();
127 	_entry->dec_count();
128 	assert(_entry->usage_count() == 0);
129 	if (parent() != this) {
130 		parent()->dec_count();
131 		parent()->remove_child(this);
132 	}
133 	else {
134 		cache_remove(_entry->name());
135 		delete _entry;
136 	}
137 
138 	assert(cache_num_open() == 0);
139 
140 	delete[] a_path;
141 	delete[] dbfile;
142 	assert(fd == -1);
143 	assert(!batch_mode);
144 }
145 
add_child(directory * d)146 void directory::add_child(directory * d)
147 {
148 	using util::contains;
149 	assert(!contains(children.begin(), children.end(), d));
150 	children.push_back(d);
151 }
152 
remove_child(directory * d)153 void directory::remove_child(directory * d)
154 {
155 	using util::contains;
156 	assert(contains(children.begin(), children.end(), d));
157 	children.erase(std::remove(children.begin(), children.end(),
158 					d),
159 			children.end());
160 }
161 
162 /**
163  * Begin a series of operations while database file is locked
164  */
batch_start()165 int directory::batch_start()
166 {
167 	DEBUG("directory::batch_start(): [%p]\n", this);
168 	if (batch_mode || fd > -1) {
169 		DEBUG("%s: oops, you screwed up\n", __PRETTY_FUNCTION__);
170 		abort();
171 	}
172 
173 	int r = opendb();
174 	if (r < 0) {
175 		DEBUG("%s: opendb() failed: %d\n", __PRETTY_FUNCTION__, r);
176 		return r;
177 	}
178 
179 	batch_mode = true;
180 	return 0;
181 }
182 
183 /**
184  * End the series of operations.
185  */
batch_end()186 int directory::batch_end()
187 {
188 	DEBUG("directory::batch_end(): [%p]\n", this);
189 	if (!batch_mode || fd < 0) {
190 		DEBUG("%s: oops, you screwed up\n", __PRETTY_FUNCTION__);
191 		abort();
192 	}
193 
194 	batch_mode = false;
195 	return closedb();
196 }
197 
198 /**
199  * Get the database entry for a file.
200  * Ensures that the file exists both in the database and on disk.  All other
201  * checks and operations (e.g. checking permissions, opening) are
202  * done at a higher level.
203  *
204  * Returns a new file_entry object upon success.  On error, returns null
205  * and err is set to the appropriate error code.
206  */
disk_lookup(const char * file,int * err)207 file_entry * directory::disk_lookup(const char * file, int * err)
208 {
209 	int r = opendb();
210 	if (r < 0) {
211 		*err = r;
212 		return 0;
213 	}
214 
215 	/** ensure sanity of filename **/
216 	if (!is_legal_name(file)) {
217 		*err = ERR_ILLEGAL_NAME;
218 		closedb();
219 		return 0;
220 	}
221 
222 	raw_entry e;
223 	memset(&e, 0, sizeof(e));
224        	r = locate_entry(file, &e, MATCH_LITERAL);
225 	if (r < 0) {
226 		*err = r;
227 		delete[] e.string_data;
228 		closedb();
229 		return 0;
230 	}
231 
232 	file_entry * fe = new file_entry(this);
233 	fill_file_entry(&e, fe);
234 	delete[] e.string_data;
235 
236 	// TODO: check that we can read on disk entry
237 	r = closedb();
238 	if (r < 0) {
239 		*err = r;
240 		delete fe;
241 		return NULL;
242 	}
243 	return fe;
244 }
245 
246 /**
247  * Write entry to database file.
248  * If 'create' is true, the entry is written even if it can't be found.
249  */
write(const file_entry * fe,bool create)250 int directory::write(const file_entry * fe, bool create)
251 {
252 	int r = opendb();
253 	if (r < 0) {
254 		return r;
255 	}
256 
257 	/* ensure legality of name */
258 	if (!is_legal_name(fe->name())) {
259 		closedb();
260 		return ERR_ILLEGAL_NAME;
261 	}
262 
263 	raw_entry e;
264 	memset(&e, 0, sizeof(e));
265 
266 	r = locate_entry(fe->name(), &e, MATCH_LITERAL);
267 	delete[] e.string_data;
268 	e.string_data = NULL;
269 
270 	if ((r == ERR_DOESNT_EXIST && !create)
271 	     || (r < 0 && r != ERR_DOESNT_EXIST)) {
272 		closedb();
273 		return r;
274 	}
275 
276 	// save these
277 	off_t old_offset = e.offset;
278 	unsigned old_padding = e.header.padding_length;
279 	unsigned old_dyn     = calc_dynlen(&e.header);
280 	unsigned old_total   = calc_entry_len(&e.header);
281 
282 	// assemble new structure
283 	size_t dyn_len = fill_header(fe, &e.header);
284 	e.string_data = new char[dyn_len];
285 	fill_dynamic(fe, e.string_data);
286 
287 	// special case where entry is last one in the file.
288 	if (old_offset > 0 &&
289 		(old_offset + old_total) == (unsigned) last_size) {
290 		DEBUG("directory::write() [%p]: '%s': detected write to last entry in database\n",
291 				this, fe->name());
292 		off_t new_size = old_offset + dyn_len + sizeof(e.header);
293 		if (new_size != last_size) {
294 			ftruncate(fd, new_size);
295 			last_size = new_size;
296 		}
297 		e.header.padding_length = 0;
298 	}
299 	else if (dyn_len > (old_dyn + old_padding)) {
300 		// writing a brand new entry or we need more space
301 		lseek(fd, sizeof(dbfile_header), SEEK_SET);
302 		r = find_free_entry(dyn_len, &e);
303 		if (r < 0) {
304 			// failed: shouldn't happen unless there is
305 			// corruption or other serious problem.
306 			delete[] e.string_data;
307 			closedb();
308 			return r;
309 		}
310 		if (old_offset != 0) {
311 			free_entry(old_offset);
312 		}
313 		e.header.padding_length -= dyn_len;
314 	}
315 	else {
316 		// normal case: writing to sufficiently-spaced entry in the middle of the database
317 		e.header.padding_length = (old_padding + old_dyn) - dyn_len;
318 	}
319 
320 	r = write_entry(&e);
321 	delete[] e.string_data;
322 	closedb();
323 	return r;
324 }
325 
326 /**
327  * Removes a file entry from the database, optionally removing it
328  * from disk as well.
329  */
unlink(const char * name,bool remove)330 int directory::unlink(const char * name, bool remove)
331 {
332 	int r = opendb();
333 	if (r < 0) {
334 		DEBUG("directory::unlink() [%p]: unlink of '%s' failed!\n", this, name);
335 		return r;
336 	}
337 
338 	/** ensure legality of name **/
339 	if (!is_legal_name(name)) {
340 		closedb();
341 		return ERR_ILLEGAL_NAME;
342 	}
343 
344 	raw_entry e;
345 	memset(&e, 0, sizeof(e));
346 
347 	r = locate_entry(name, &e, MATCH_LITERAL);
348 	delete[] e.string_data;
349 
350 	if (r < 0) {
351 		DEBUG("directory::unlink() [%p]: unable to find '%s'\n", this, name);
352 		closedb();
353 		return r;
354 	}
355 	free_entry(e.offset);
356 	closedb();
357 
358 	DEBUG("directory::unlink() [%p]: erased entry '%s' at offset %ld\n", this, name, e.offset);
359 
360 	if (remove) {
361 		char * file = NULL;
362 		my_asprintf(&file, "%s/%s", abs_path(), name);
363 		::unlink(file);
364 		delete[] file;
365 	}
366 	return 0;
367 }
368 
369 /**
370  * List files in the database that match pattern.  Return results in
371  * an STL vector.
372  */
ls(const char * pattern,std::vector<entry_data> & results)373 int directory::ls(const char * pattern, std::vector<entry_data>& results)
374 {
375 	int r = opendb();
376 	if (r < 0) {
377 		DEBUG("directory::unlink() [%p]: ls of '%s' failed!\n", this, pattern);
378 		return r;
379 	}
380 
381 	/**
382 	 * Ensure legality of pattern (really needed?)
383 	 */
384 	if (!is_legal_name(pattern)) {
385 		closedb();
386 		return ERR_ILLEGAL_NAME;
387 	}
388 
389 	results.reserve(25);
390 
391 	while (true) {
392 		raw_entry e;
393 		memset(&e, 0, sizeof(e));
394 
395 		r = locate_entry(pattern, &e, MATCH_WILDCARD);
396 		if (r < 0) {
397 			closedb();
398 			delete[] e.string_data;
399 			/** check if error was from corruption, otherwise return 0 **/
400 			return (r == ERR_DOESNT_EXIST) ? 0 : r;
401 		}
402 		file_entry fe(this);
403 		fill_file_entry(&e, &fe);
404 		delete[] e.string_data;
405 
406 		fe.update_size();
407 
408 		// XXX: kind of wasteful; another copy of the data will be made
409 		// for storage in the vector (and potentially more for vector
410 		// resizes), but this is the simplest approach for now
411 		const entry_data * data = fe.raw_data();
412 		results.push_back(*data);
413 	}
414 	/* not reached! */
415 	return 0;
416 }
417 
418 /**
419  * Delete database file from disk.
420  */
destroy()421 int directory::destroy()
422 {
423 	if (usage_count() > 1 || fd > -1) {
424 		return ERR_BUSY;
425 	}
426 
427 	if (::unlink(dbfile) < 0) {
428 		DEBUG("directory::destroy() [%p]: Unable to delete '%s': %s\n", this, DB_FILE_NAME, strerror(errno));
429 		return ERR_DB_DESTROY;
430 	}
431 	DEBUG("directory::destroy() [%p]: database file '%s' deleted\n", this, dbfile);
432 	// FIXME: what else to do here?
433 	return 0;
434 }
435 
436 /**
437  * Checks if database file exists
438  */
exists() const439 bool directory::exists()  const
440 {
441 	int j = access(dbfile, R_OK | W_OK);
442 	return (j == 0);
443 }
444 
445 /**
446  * Confirm legality of database file.
447  */
validate()448 int directory::validate()
449 {
450 	int r = opendb();
451 	if (r < 0) {
452 		return r;
453 	}
454 	closedb();
455 	return 0;
456 }
457 
458 /**
459  * Tests whether any files are listed in the database.
460  * Return:
461  *	< 0 : access error
462  *       0 : false
463  *       1 : true
464  */
is_empty()465 int directory::is_empty()
466 {
467 	int r = opendb();
468 	if (r < 0) {
469 		return r;
470 	}
471 
472 	struct raw_entry e;
473 	memset(&e, 0, sizeof(e));
474 	r = locate_entry("*", &e, MATCH_WILDCARD);
475 	delete[] e.string_data;
476 	closedb();
477 
478 	if (r == ERR_DOESNT_EXIST) {
479 		return 1;
480 	}
481        	else if (r < 0) {
482 		return r;
483 	}
484 	/* files were found .. */
485 	return 0;
486 }
487 
488 /**
489  * Translate directory error codes.
490  */
strerror(int err)491 /* static */ const char * directory::strerror(int err)
492 {
493 	switch (err) {
494 	case ERR_ILLEGAL_NAME:
495 		return "Illegal filename";
496 	case ERR_DB_CORRUPTION:
497 		return "Corrupted database file detected";
498 	case ERR_BUSY:
499 		return "File or directory is in use";
500 	case ERR_FAILURE:
501 		return "Operation failed";
502 	case ERR_DB_VERSION:
503 		return "Incompatible database version";
504 	case ERR_DB_OPEN:
505 		return "Unable to open database";
506 	case ERR_DB_EXISTS:
507 		return "A database file already exists";
508 	case ERR_DB_DESTROY:
509 		return "Unable to delete database file";
510 	case ERR_BAD_WRITE:
511 		return "Tried to write invalid entry!";
512 	}
513 	return "Unknown error";
514 }
515 
516 /**
517  * Creates a new database file.
518  */
create()519 int directory::create()
520 {
521 	if (exists()) {
522 		DEBUG("directory::create(): database file '%s' already exists!\n", dbfile);
523 		return ERR_DB_EXISTS;
524 	}
525 
526 	fd = open(dbfile, O_CREAT | O_EXCL | O_WRONLY, 0600);
527 	if (fd < 0) {
528 		DEBUG("directory::create(): '%s': open() syscall failed!\n", dbfile);
529 		return ERR_IO_WRITE;
530 	}
531 
532 	/** Write header and exit **/
533 	write_header(time(NULL));
534 	close(fd);
535 	fd = -1;
536 	DEBUG("directory::create(): '%s': new database file created\n", dbfile);
537 	return 0;
538 }
539 // implementation follows.
540 
541 /**
542  * Open database file in the directory & lock it.
543  * Assumes database file has been created and updated to the current version!
544  */
opendb()545 int directory::opendb()
546 {
547 	if (batch_mode) {
548 		lseek(fd, sizeof(dbfile_header), SEEK_SET);
549 		return fd;
550 	}
551 
552 	fd = open(dbfile, O_RDWR);
553 	if (fd < 0) {
554 		DEBUG("directory::opendb(): unable to open database\n");
555 		fd = -1;
556 		return ERR_DB_OPEN;
557 	}
558 
559 	/** lock it. **/
560 	lockf(fd, F_LOCK, 0);
561 
562 	dbfile_header h;
563 	memset(&h, 0, sizeof(h));
564 	if (read_header(&h) < 0) {
565 		DEBUG("directory::opendb(): database is corrupted.\n");
566 		close(fd);
567 		fd = -1;
568 		return ERR_DB_CORRUPTION;
569 	}
570 
571 	if (h.version > DB_VERSION) {
572 		DEBUG("directory::opendb(); incorrect version\n");
573 		close(fd);
574 		fd = -1;
575 		return ERR_DB_VERSION;
576 	}
577 
578 	struct stat st;
579 	fstat(fd, &st);
580 
581 	last_mod = st.st_mtime;
582 	last_size = st.st_size;
583 
584 	/** success **/
585 	return fd;
586 }
587 
588 /**
589  * Closes and unlocks database file.  Writes the top header, but nothing else.
590  */
closedb()591 int directory::closedb()
592 {
593 	if (batch_mode) {
594 		return 0;
595 	}
596 
597 	write_header(time(NULL));
598 	fsync(fd);
599 	lockf(fd, F_ULOCK, 0);
600 	close(fd);
601 	fd = -1;
602 	return 0;
603 }
604 
605 /**
606  * Writes the header at the top of the database file.  Repositions file pointer
607  * to the beginning of the file first.
608  */
write_header(time_t timestamp)609 int directory::write_header(time_t timestamp)
610 {
611 	dbfile_header h;
612 	memset(&h, 0, sizeof(h));
613 	h.version = DB_VERSION;
614 	h.timestamp = timestamp;
615 	strcpy(h.hstring, "ezb fdbh");
616 	lseek(fd, SEEK_SET, 0);
617 	if (::write(fd, &h, sizeof(h)) == sizeof(h)) {
618 		return 0;
619 	}
620 	return -1;
621 }
622 
623 
624 /**
625  * Return the number of files in the cache that are currently in use
626  */
cache_num_open() const627 int directory::cache_num_open() const
628 {
629 	int count = 0;
630 	hash_table_t::const_iterator i = file_cache.begin(),
631 					e = file_cache.end();
632 	for (; i != e ; ++i) {
633 		file_entry * e = (*i).second;
634 		if (e->usage_count() > 0) {
635 			++count;
636 		}
637 	}
638 	return count;
639 }
640 
641 /**
642  * Remove unused entries from the open file cache.
643  */
cache_prune()644 int directory::cache_prune()
645 {
646 	DEBUG("directory::cache_prune(): [%p] entering for %s\n", this, rel_path());
647 	hash_table_t::iterator i = file_cache.begin(),
648 					e = file_cache.end();
649 	while (i != e) {
650 		file_entry * e = (*i).second;
651 		if (e->usage_count() == 0) {
652 			i = file_cache.erase(i);
653 			delete e;
654 		}
655 		else {
656 			++i;
657 		}
658 	}
659 	return file_cache.size();
660 }
661 
662 /**
663  * Read header from top of the database file.  Assumes file pointer
664  * is at the beginning of the file.
665  */
read_header(dbfile_header * h)666 int directory::read_header(dbfile_header * h)
667 {
668 	assert(h != NULL);
669 	assert(fd != -1);
670 
671 	int r = read(fd, h, sizeof(dbfile_header));
672 
673 	if (r < (signed) sizeof(dbfile_header)) {
674 		return -1;
675 	}
676 
677 	if (strncmp(h->hstring, "ezb fdbh", 8)) {
678 		return -1;
679 	}
680 
681 	return 0;
682 }
683 
684 /**
685  * Look for a entry on disk matching "pattern".
686  * Return ERR_DOESNT_EXIST if not found.
687  */
locate_entry(const char * pattern,struct raw_entry * r,int match_options)688 int directory::locate_entry(const char * pattern, struct raw_entry * r, int match_options)
689 {
690 	assert(fd > -1);
691 	int j = 0;
692 	int (*matcher)(const char *, const char *);
693 
694 	matcher = (match_options & MATCH_LITERAL) ? &strcmp : &wild_match;
695 	off_t offset = lseek(fd, 0, SEEK_CUR);
696 
697 	while (true) {
698 		j = read(fd, &r->header, sizeof(r->header));
699 		if (j == 0) {
700 			// EOF
701 			r->offset = 0;
702 			memset(&r->header, 0, sizeof(r->header));
703 			return ERR_DOESNT_EXIST;
704 		}
705 		else if (j < 0) {
706 			return ERR_IO_READ;
707 		}
708 		else if (j < (signed) sizeof(r->header)) {
709 			// premature eof
710 			return ERR_DB_CORRUPTION;
711 		}
712 		else if (validate_header(&r->header) != 0) {
713 			// bad header -- corrupted file?
714 			return ERR_DB_CORRUPTION;
715 		}
716 
717 		// empty block?
718 		if (r->header.flags & FE_EMPTY_ENTRY) {
719 			offset = lseek(fd, r->header.padding_length, SEEK_CUR);
720 			continue;
721 		}
722 
723 		// header ok, now read dynamic data
724 		unsigned short dyn_length = calc_dynlen(&r->header);
725 		r->offset = offset;
726 		r->string_data = new char[dyn_length];
727 		j = read(fd, r->string_data, dyn_length);
728 		if (j < (signed) dyn_length) {
729 			// premature EOF or read error.
730 			delete[] r->string_data;
731 			r->string_data = NULL;
732 			if (j < 0) {
733 				return ERR_IO_READ;
734 			}
735 			return ERR_DB_CORRUPTION;
736 		}
737 		if (validate_dynamic(&r->header, r->string_data) != 0) {
738 			delete[] r->string_data;
739 			r->string_data = NULL;
740 			return ERR_DB_CORRUPTION;
741 		}
742 
743 		// goto end of record
744 		offset = lseek(fd, r->header.padding_length, SEEK_CUR);
745 
746 		// make sure this is the file we're looking for
747 		const char * filename = &r->string_data[dyn_length - r->header.name_length];
748 		if (matcher(pattern, filename) != 0) {
749 			delete[] r->string_data;
750 			r->string_data = NULL;
751 			continue;
752 		}
753 
754 		// found a match.
755 		break;
756 	}
757 	return 0;
758 }
759 
760 
761 /**
762  * Find a free block in the database file of at least size 'size'.
763  * Offset of found block is stored in r->offset.
764  * Size of found block is stored in r->header.padding_length.
765  * No other fields of 'r' are modified.
766  * File pointer is positioned at the beginning of the free entry.
767  * Consolidates sequences of empty blocks if possible.
768  * If nothing is found, result offset is set to the end of the file.
769  * Return 0 on success.
770  *
771  * FIXME: currently does not utilize padding as free space.
772  * (i.e., only seeks entries marked as FE_EMPTY_ENTRY)
773  */
find_free_entry(unsigned int size,raw_entry * r)774 int directory::find_free_entry(unsigned int size, raw_entry * r)
775 {
776 	struct raw_entry free_block, current;
777 	bool free_found = false;
778 	int  j = 0;
779 	off_t offset = lseek(fd, 0, SEEK_CUR);
780 
781 	memset(&free_block, 0, sizeof (raw_entry));
782 	memset(&current, 0, sizeof (raw_entry));
783 
784 	while (true) {
785 		current.offset = offset;
786 		j = read(fd, &current.header, sizeof (current.header));
787 		if (j == 0) {
788 			/** EOF **/
789 			break;
790 		}
791 		else if (j < 0) {
792 			return ERR_IO_READ;
793 		}
794 		else if (j < (signed) sizeof(current.header)) {
795 			return ERR_DB_CORRUPTION;
796 		}
797 		else if (validate_header(&current.header) != 0) {
798 			return ERR_DB_CORRUPTION;
799 		}
800 		offset += j;
801 
802 		if (free_found) {
803 			if (current.header.flags & FE_EMPTY_ENTRY) {
804 				/**
805 				 * Found two empty blocks next to each other.
806 				 * Consolidate them -- but make sure the resultant free entry
807 				 * does not exceed any size limits.
808 				 */
809 				DEBUG("find_free_entry(): consolidating empty blocks:\n");
810 				DEBUG("                   block 1: %d bytes @ offset %ld  | block 2: %d bytes @ offset %ld\n",
811 							free_block.header.padding_length, free_block.offset,
812 							current.header.padding_length, current.offset);
813 
814 				unsigned block1_size = calc_entry_len(&free_block.header);
815 				unsigned block2_size = calc_entry_len(&current.header);
816 				if (block1_size + block2_size <= MAX_ENTRY_SIZE) {
817 					free_block.header.padding_length += block2_size;
818 					lseek(fd, free_block.offset, SEEK_SET);
819 					// XXX: unchecked write
820 					::write(fd, &free_block.header, sizeof (free_block.header));
821 					offset = lseek(fd, free_block.header.padding_length, SEEK_CUR);
822 					continue;
823 				}
824 				// the resulting combined entry would have been too large ...
825 				DEBUG("find_free_entry(): skipped consolidation; total was %u (max %zd)\n",
826 								block1_size + block2_size, MAX_ENTRY_SIZE);
827 			}
828 
829 			/**
830 			 * End of free space: check if it will fit here.
831 			 */
832 			if (free_block.header.padding_length >= size) {
833 				DEBUG("find_free_entry(): Using free block of %d bytes @ offset %ld\n",
834 						free_block.header.padding_length, free_block.offset);
835 				r->offset = free_block.offset;
836 				r->header.padding_length = free_block.header.padding_length;
837 				lseek(fd, free_block.offset, SEEK_SET);
838 				return 0;
839 			}
840 			// not big enough
841 			offset = lseek(fd, current.header.padding_length + calc_dynlen(&current.header), SEEK_CUR);
842 			free_found = false;
843 			continue;
844 		}
845 
846 		// Still looking for a free block
847 		if (current.header.flags & FE_EMPTY_ENTRY) {
848 			free_block.offset = current.offset;
849 			free_block.header.padding_length = current.header.padding_length;
850 			free_block.header.flags = FE_EMPTY_ENTRY;
851 			free_found = true;
852 		}
853 
854 		// Nothing
855 		offset = lseek(fd, current.header.padding_length + calc_dynlen(&current.header), SEEK_CUR);
856 	}
857 
858 	/**
859 	 * EOF reached.
860 	 */
861 	if (free_found) {
862 		/**
863 		 * Last block was free one:
864 		 * just truncate to the start of the free block.
865 		 */
866 		DEBUG("find_free_entry(): found free space at end of file with offset=%ld, truncating file\n", free_block.offset);
867 		j = ftruncate(fd, free_block.offset);
868 		if (j < 0) {
869 			// This might happen if filesystem becomes full ?
870 			DEBUG("find_free_entry(): ftruncate() failed!\n");
871 			return ERR_IO_WRITE;
872 		}
873 	}
874 	r->offset = lseek(fd, 0, SEEK_END);
875 	r->header.padding_length = size;
876 	return 0;
877 }
878 
879 /**
880  * Zap the entry at 'offset', marking it as a free block.
881  */
free_entry(off_t offset)882 int directory::free_entry(off_t offset)
883 {
884 	assert(offset > 0);
885 	struct raw_entry e;
886 	memset(&e, 0, sizeof(e));
887 	lseek(fd, offset, SEEK_SET);
888 	int j = read(fd, &e.header, sizeof (e.header));
889 	if (j < 0) {
890 		return ERR_IO_READ;
891 	}
892        	else if (j < (signed) sizeof(e.header)) {
893 		return ERR_DB_CORRUPTION;
894 	}
895 	else if (validate_header(&e.header) != 0) {
896 		return ERR_DB_CORRUPTION;
897 	}
898 
899 	unsigned int new_padding = e.header.padding_length + calc_dynlen(&e.header);
900 	memset(&e, 0, sizeof(e));
901 
902 	e.header.flags = FE_EMPTY_ENTRY;
903 	e.header.padding_length = new_padding;
904 	char * zeroes = new char[new_padding];
905 	memset(zeroes, 0, new_padding);
906 
907 	lseek(fd, -(sizeof (e.header)), SEEK_CUR);
908 	// XXX: unchecked writes
909 	::write(fd, &e.header, sizeof(e.header));
910 	::write(fd, zeroes, new_padding);
911 	delete[] zeroes;
912 	return 0;
913 }
914 
915 /**
916  * Write this entry to database file.
917  * raw_entry r determines what gets written where (at r->offset).
918  * Overwrites any data at the spot and assumes there is sufficient space for
919  * the entry.
920  * NOTE: validates header and dynamic data block before writing.
921  *
922  * @param r 	pointer to raw_entry, which will be validated
923  * @return 0 on success
924  */
write_entry(const struct raw_entry * r)925 int directory::write_entry(const struct raw_entry * r)
926 {
927 	assert(r->offset > 0);
928 	int j = lseek(fd, r->offset, SEEK_SET);
929 	if (j < 0) {
930 		return ERR_DB_CORRUPTION;
931 	}
932 	if (validate_header(&r->header) != 0) {
933 		return ERR_BAD_WRITE;
934 	}
935 	if (validate_dynamic(&r->header, r->string_data) != 0) {
936 		return ERR_BAD_WRITE;
937 	}
938 
939 	unsigned int dyn = calc_dynlen(&r->header);
940 	unsigned int padding = r->header.padding_length;
941 	unsigned int total = sizeof(r->header) + dyn + padding;
942 
943 	// TODO: use writev() here?
944 	char * buffer = new char[total];
945 	memset(buffer, 0, total);
946 	memcpy(buffer, &r->header, sizeof(r->header));
947 	memcpy(buffer + sizeof(r->header), r->string_data, dyn);
948 
949 	j = ::write(fd, buffer, total);
950 	delete[] buffer;
951 
952 	if (j < (signed) total) {
953 		return ERR_IO_WRITE;
954 	}
955 
956 	/** update known filesize **/
957 	if (r->offset + (signed) total > last_size) {
958 		last_size = r->offset + total;
959 	}
960 	DEBUG("write_entry(): wrote entry @ offset %ld size: %u bytes [%zd header, %d dyn, %d padding]\n",
961 			r->offset, total, sizeof(r->header), dyn, padding);
962 	return 0;
963 }
964 
is_legal_name(const char * name)965 /* static */ bool directory::is_legal_name(const char * name)
966 {
967 	return (is_non_empty(name) && strcmp(name, DB_FILE_NAME) != 0
968 			&& !strchr(name, '/'));
969 }
970 
971 /**
972  * Fill a entry header with information from a file_entry -- creation time, flags,
973  * downloads, and the string data.
974  *
975  * @param e	file_entry to read
976  * @param h 	entry_header to populate
977  * @return length of the string data.
978  */
fill_header(const file_entry * e,entry_header * h)979 /* static */ size_t directory::fill_header(const file_entry * e, entry_header * h)
980 {
981 	assert(h);
982 	memset(h, 0, sizeof(entry_header));
983 
984 	size_t dyn = 0;
985 	h->creation	= e->creation();
986 	h->flags	= e->flags();
987 	h->mode		= e->mode();
988 
989 	// The owner and group are stored in the same field,
990 	// separated by a colon.
991 	if (is_non_empty(e->owner())) {
992 		h->owner_length = strlen(e->owner()) + 1;
993 		if (is_non_empty(e->group())) {
994 			// space set aside for owner's null byte will
995 			// be used for the colon
996 			h->owner_length += strlen(e->group()) + 1;
997 		}
998 		dyn += h->owner_length;
999 	}
1000 	if (is_non_empty(e->misc())) {
1001 		dyn += (h->misc_length = strlen(e->misc()) + 1);
1002 	}
1003 	if (is_non_empty(e->desc())) {
1004 		dyn += (h->desc_length = strlen(e->desc()) + 1);
1005 	}
1006 	if (is_non_empty(e->name())) {
1007 		dyn += (h->name_length = strlen(e->name()) + 1);
1008 	}
1009 	if (is_non_empty(e->from())) {
1010 		dyn += (h->from_length = strlen(e->from()) + 1);
1011 	}
1012 	return dyn;
1013 }
1014 
1015 /**
1016  * Create raw data buffer to be written to the database file.
1017  * The buffer must be as large as all of the length fields
1018  * combined. (which fill_header() will return)
1019  * @param e 	file_entry to read
1020  * @param b 	buffer to fill
1021  * @return the number of bytes written
1022  */
fill_dynamic(const file_entry * e,char * b)1023 /* static */ size_t directory::fill_dynamic(const file_entry * e, char * b)
1024 {
1025 	unsigned offset = 0;
1026 	unsigned len = 0;
1027 	char * user = NULL;
1028 
1029 	my_asprintf(&user, "%s:%s", e->owner(), e->group());
1030 	const char * table[] = {
1031 		user, e->from(), e->misc(), e->desc(), e->name()
1032 	};
1033 
1034 	for (unsigned j = 0; j < 5; ++j) {
1035 		len = (table[j] ? strlen(table[j]) : 0);
1036 		if (len > 0) {
1037 			memcpy(b + offset, table[j], len);
1038 			*(b+offset + len) = 0;
1039 			offset += len+1;
1040 		}
1041 	}
1042 
1043 	delete[] user;
1044 	return offset;
1045 }
1046 
1047 /**
1048  * Fill a file_entry with information from an entry in the database.
1049  * Assumes that the header and dynamic data portions of the raw_entry
1050  * have already been validated.  In particular, the string-terminating
1051  * null characters *must* be at the correct locations in the dynamic data block.
1052  *
1053  * @param r	raw_entry containg header and dynamic string data
1054  * @param e	file_entry to assign values to
1055  */
fill_file_entry(const raw_entry * r,file_entry * e)1056 /* static */ void directory::fill_file_entry(const raw_entry * r, file_entry * e)
1057 {
1058 	assert(r != NULL);
1059 	assert(e != NULL);
1060 	DEBUG("%s [%p]\n", __PRETTY_FUNCTION__, e);
1061 
1062 	const char * raw = r->string_data;
1063 	const struct entry_header * h = &r->header;
1064 	unsigned int offset = 0;
1065 	bool old_version = false;
1066 
1067 	/**
1068 	 * First set user/group information.
1069 	 * In older versions, group information did not exist.  Now, the group is appended
1070 	 * to username, with a colon separating them.  If the older format is encountered,
1071 	 * assign a default value to the group field.
1072 	 */
1073 	const char * colon = strchr(raw, ':');
1074 	if (colon != NULL) {
1075 		// new format
1076 		int offset = colon - raw;
1077 		char * user = my_strdup(raw);
1078 		user[offset] = 0;
1079 
1080 		const char * group = user+offset+1;
1081 
1082 		assert(strlen(user) > 0);
1083 		assert(strlen(group) > 0);
1084 		e->set_owner(user);
1085 		e->set_group(group);
1086 
1087 		delete[] user;
1088 	}
1089 	else {
1090 		// old format: set a default group
1091 		old_version = true;
1092 		e->set_owner(raw);
1093 		e->set_group(raw);
1094 	}
1095 	offset += h->owner_length;
1096 
1097 	if (h->from_length > 0) {
1098 		e->set_from(&raw[offset]);
1099 	}
1100 	offset  += h->from_length;
1101 
1102 	if (h->misc_length > 0) {
1103 		e->set_misc(&raw[offset]);
1104 	}
1105 	offset += h->misc_length;
1106 
1107 	if (h->desc_length > 0) {
1108 		e->set_desc(&raw[offset]);
1109 	}
1110 	offset += h->desc_length;
1111 
1112 	if (raw[offset]) {
1113 		e->set_name(&raw[offset]);
1114 	}
1115 
1116 	e->set_creation(h->creation);
1117 	e->set_flags(h->flags);
1118 	e->set_mode(h->mode);
1119 
1120 	// backward compatiblity hacks
1121 	// assign sane permissions to file if it didn't have any
1122 	if (old_version) {
1123 		DEBUG("directory::fill_file_entry(): detected old format for entry '%s' (owner: '%s' mode: %d')\n",
1124 					e->name(), e->owner(), e->mode());
1125 		if (strcasecmp(e->owner(), "(system)") == 0) {
1126 			e->set_owner("root");
1127 			e->set_group("root");
1128 		}
1129 		if (!e->mode()) {
1130 			if (e->flags() & FE_DIRECTORY) {
1131 				e->set_mode(0755);
1132 			}
1133 			else {
1134 				e->set_mode(0600);
1135 			}
1136 		}
1137 	}
1138 	//--------------------------------------------------
1139 	// DEBUG("\t---> new entry: name: '%s' owner: %s\n", name(), owner());
1140 	// DEBUG("\t  \\--> pass: %s desc: %s from: %s\n", misc(), desc(), from());
1141 	// DEBUG("\t  \\--> creation: %ld flags: %d\n", _creation, _flags);
1142 	//--------------------------------------------------
1143 }
1144 
1145 /**
1146  * Check legality of a entry header.
1147  * Header legality rules:
1148  * 	- Unfortunately there is no magic number for entry headers.
1149  * 	- The total advertised size of the entry must be <= MAX_ENTRY_SIZE.
1150  * 	- Each field's length must not exceed its limit.
1151  * 	- The name length must be >= 2
1152  * 	- The owner length cannot be >= 2
1153  * 	- Special exceptions for "Empty" entries
1154  *
1155  * Empty Entry header rules:
1156  * 	- Padding must be > 0 <= MAX_PADDING_LEN
1157  * 	- Creation field may be non-zero, all other fields must be zero.
1158  *
1159  * @param h 	pointer to entry_header struct to validate
1160  * @return 0 if the header appears legal
1161  */
validate_header(const entry_header * h)1162 /* static */ int directory::validate_header(const entry_header * h)
1163 {
1164 	using util::in_range;
1165 
1166 	if (h->flags & FE_EMPTY_ENTRY) {
1167 		// empty entry
1168 		const bool padding_ok = h->padding_length > 0
1169 						&& h->padding_length <= MAX_PADDING_LEN;
1170 		const bool no_dyn = (calc_dynlen(h) == 0);
1171 		const bool fields_ok = !h->mode && !h->reserved1;
1172 
1173 		if (padding_ok && no_dyn && fields_ok) {
1174 			assert(calc_entry_len(h) <= MAX_ENTRY_SIZE);
1175 			return 0;
1176 		}
1177 		return -1;
1178 	}
1179 	// other entries
1180 	if (calc_entry_len(h) > MAX_ENTRY_SIZE) {
1181 		return -1;
1182 	}
1183 
1184 	const bool name_ok = in_range(h->name_length, MIN_NAME_LEN, MAX_NAME_LEN);
1185 	const bool owner_ok = in_range(h->owner_length, MIN_OWNER_LEN, MAX_OWNER_LEN);
1186 	const bool misc_ok = in_range(h->misc_length, MIN_MISC_LEN, MAX_MISC_LEN);
1187 	const bool desc_ok = in_range(h->desc_length, MIN_DESC_LEN, MAX_DESC_LEN);
1188 	const bool from_ok = in_range(h->from_length, MIN_FROM_LEN, MAX_FROM_LEN);
1189 	const bool padding_ok = in_range(h->padding_length, MIN_PADDING_LEN, MAX_PADDING_LEN);
1190 
1191 	// TODO: check value of reserved1 field?
1192 	if (name_ok && owner_ok && misc_ok &&
1193 			desc_ok && from_ok && padding_ok) {
1194 		return 0;
1195 	}
1196 	return -1;
1197 }
1198 
1199 /**
1200  * Given a legal entry_header, determine if a dynamic data block
1201  * is legal.  Currently this only involves checking if the string
1202  * terminating null characters are at the right locations.
1203  */
validate_dynamic(const entry_header * h,const char * data)1204 /* static */ int directory::validate_dynamic(const entry_header * h, const char * data)
1205 {
1206 	const unsigned offsets[] = {
1207 		h->owner_length,
1208 		h->from_length,
1209 		h->misc_length,
1210 		h->desc_length,
1211 		h->name_length
1212 	};
1213 
1214 	/**
1215 	 * This is a bit tricky, since the length of a dynamic field may be zero,
1216 	 * but if it's not zero, it always includes 1 byte for the terminating '\0'
1217 	 */
1218 	for (unsigned i = 0, j = 0; i < (sizeof offsets / sizeof(offsets[0])); ++i) {
1219 		j += offsets[i];
1220 		if (offsets[i] != 0) {
1221 			if (data == NULL) {
1222 				// XXX: is this necessary?
1223 				return -1;
1224 			}
1225 			if (data[j-1] != 0) {
1226 				return -1;
1227 			}
1228 		}
1229 	}
1230 	return 0;
1231 }
1232 
1233 #ifdef __DEBUG__
1234 /**
1235   * Print listing of raw entries to a file descriptor
1236   */
dump(int out)1237 int directory::dump(int out)
1238 {
1239 	using io::fdprintf;
1240 	int j = opendb();
1241 	if (j < 0) {
1242 		fdprintf(out, "Unable to open database: %s\n", this->strerror(j));
1243 		return j;
1244 	}
1245 	std::string ts;
1246 	timestamp_full(ts, last_mod);
1247 
1248 	fdprintf(out, "***************************************************************************\n");
1249 	fdprintf(out, "Analyzing database file: %s\n", dbfile);
1250 	fdprintf(out, "size: %u timestamp: %s\n", last_size, ts.c_str());
1251 	fdprintf(out, "abs. path: %s\n", abs_path());
1252 	fdprintf(out, "rel. path: %s\n", rel_path());
1253 	fdprintf(out, "\nDumping entries...\n");
1254 
1255 	off_t offset = lseek(fd, 0, SEEK_CUR);
1256 	struct raw_entry r;
1257 
1258 	while (true) {
1259 		fdprintf(out, "\n---------------------------------------------------\n"
1260 				"offset: %d [0x%x]\n", offset, offset);
1261 		memset(&r, 0, sizeof(r));
1262 		j = read(fd, &r.header, sizeof(r.header));
1263 		if (j == 0) {
1264 			// EOF
1265 			break;
1266 		}
1267 		else if (j < (signed) sizeof(r.header)) {
1268 			// premature eof or read error
1269 			if (j < 0) {
1270 				fdprintf(out, "* ERROR: read() failed: %s\n", ::strerror(errno));
1271 			}
1272 			else {
1273 				fdprintf(out, "* ERROR: premature EOF: corruption detected!\n");
1274 			}
1275 			closedb();
1276 			return ERR_DB_CORRUPTION;
1277 		}
1278 		else if (validate_header(&r.header) != 0) {
1279 			return ERR_DB_CORRUPTION;
1280 		}
1281 		offset += j;
1282 
1283 		unsigned short dyn_length = calc_dynlen(&r.header);
1284 		if (r.header.flags & FE_EMPTY_ENTRY)  {
1285 			offset = lseek(fd, dyn_length + r.header.padding_length, SEEK_CUR);
1286 			fdprintf(out, "free block: %u bytes\n", r.header.padding_length);
1287 			continue;
1288 		}
1289 
1290 		fdprintf(out, "file block    : %u bytes dynamic, %u bytes padding\n", dyn_length, r.header.padding_length);
1291 		fdprintf(out, "flags         : 0x%x\n", r.header.flags);
1292 		r.string_data = new char[dyn_length];
1293 		j = read(fd, r.string_data, dyn_length);
1294 		if (j < (signed) dyn_length) {
1295 			// premature EOF or read error.
1296 			delete[] r.string_data;
1297 			if (j < 0) {
1298 				fdprintf(out, "* ERROR: read error while reading dynamic data: %s\n", ::strerror(errno));
1299 			}
1300 			else {
1301 				fdprintf(out, "* ERROR: premature EOF while reading dynamic data: corruption detected!\n");
1302 			}
1303 			closedb();
1304 			return ERR_DB_CORRUPTION;
1305 		}
1306 		offset += j;
1307 		file_entry * e = new file_entry(this);
1308 		fill_file_entry(&r, e);
1309 		timestamp_full(ts, e->creation());
1310 
1311 		fdprintf(out, "file name     : %s\n", e->name());
1312 		fdprintf(out, "owner         : %s\n", e->owner());
1313 		fdprintf(out, "from          : %s\n", e->from());
1314 		fdprintf(out, "misc          : %s\n", e->misc());
1315 		fdprintf(out, "description   : %s\n", e->desc());
1316 		fdprintf(out, "creation time : %d (%s)\n", e->creation(), ts.c_str());
1317 		fdprintf(out, "padding       : %d\n", r.header.padding_length);
1318 
1319 		delete e;
1320 		delete[] r.string_data;
1321 		offset = lseek(fd, r.header.padding_length, SEEK_CUR);
1322 	}
1323 
1324 	fdprintf(out, "\n\n *** END OF FILE ***\n");
1325 	closedb();
1326 	return 0;
1327 }
1328 
1329 #endif /* __DEBUG__ */
1330 
1331 } // namespace fs
1332