Branch data Line data Source code
1 : : /** @file chert_version.cc
2 : : * @brief ChertVersion class
3 : : */
4 : : /* Copyright (C) 2006,2007,2008,2009 Olly Betts
5 : : * Copyright 2010 Richard Boulton
6 : : *
7 : : * This program is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU General Public License as published by
9 : : * the Free Software Foundation; either version 2 of the License, or
10 : : * (at your option) any later version.
11 : : *
12 : : * This program is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : : * GNU General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU General Public License
18 : : * along with this program; if not, write to the Free Software
19 : : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 : : */
21 : :
22 : : #include <config.h>
23 : :
24 : : #include "safeerrno.h"
25 : :
26 : : #include <xapian/error.h>
27 : :
28 : : #include "chert_version.h"
29 : : #include "io_utils.h"
30 : : #include "omassert.h"
31 : : #include "stringutils.h" // For STRINGIZE() and CONST_STRLEN().
32 : : #include "str.h"
33 : :
34 : : #ifdef __WIN32__
35 : : # include "msvc_posix_wrapper.h"
36 : : #endif
37 : :
38 : : #include <cstdio> // For rename().
39 : : #include <cstring> // For memcmp() and memcpy().
40 : : #include <string>
41 : :
42 : : #include "common/safeuuid.h"
43 : :
44 : : using namespace std;
45 : :
46 : : // YYYYMMDDX where X allows multiple format revisions in a day
47 : : #define CHERT_VERSION 200912150
48 : : // 200804180 Chert debuts.
49 : : // 200903070 1.1.0 doclen bounds and wdf upper bound.
50 : : // 200912150 1.1.4 shorter position, postlist, record and termlist keys.
51 : :
52 : : #define MAGIC_STRING "IAmChert"
53 : :
54 : : #define MAGIC_LEN CONST_STRLEN(MAGIC_STRING)
55 : : // 4 for the version number; 16 for the UUID.
56 : : #define VERSIONFILE_SIZE (MAGIC_LEN + 4 + 16)
57 : :
58 : : // Literal version of VERSIONFILE_SIZE, used for error message. This needs
59 : : // to be updated by hand should VERSIONFILE_SIZE change, but that rarely
60 : : // happens so this isn't an onerous requirement.
61 : : #define VERSIONFILE_SIZE_LITERAL 28
62 : :
63 : : void
64 : 345 : ChertVersion::create()
65 : : {
66 : 345 : char buf[VERSIONFILE_SIZE] = MAGIC_STRING;
67 : 345 : unsigned char *v = reinterpret_cast<unsigned char *>(buf) + MAGIC_LEN;
68 : 345 : v[0] = static_cast<unsigned char>(CHERT_VERSION & 0xff);
69 : 345 : v[1] = static_cast<unsigned char>((CHERT_VERSION >> 8) & 0xff);
70 : 345 : v[2] = static_cast<unsigned char>((CHERT_VERSION >> 16) & 0xff);
71 : 345 : v[3] = static_cast<unsigned char>((CHERT_VERSION >> 24) & 0xff);
72 : :
73 : 345 : uuid_generate(uuid);
74 : 345 : memcpy(buf + MAGIC_LEN + 4, (void*)uuid, 16);
75 : :
76 : 345 : int fd = ::open(filename.c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
77 : :
78 [ - + ]: 345 : if (fd < 0) {
79 : 0 : string msg("Failed to create chert version file: ");
80 : 0 : msg += filename;
81 : 0 : throw Xapian::DatabaseOpeningError(msg, errno);
82 : : }
83 : :
84 : : try {
85 : 345 : io_write(fd, buf, VERSIONFILE_SIZE);
86 : 0 : } catch (...) {
87 : 0 : (void)close(fd);
88 : 0 : throw;
89 : : }
90 : :
91 [ - + ]: 345 : if (close(fd) != 0) {
92 : 0 : string msg("Failed to create chert version file: ");
93 : 0 : msg += filename;
94 : 0 : throw Xapian::DatabaseOpeningError(msg, errno);
95 : : }
96 : 345 : }
97 : :
98 : : void
99 : 1980 : ChertVersion::read_and_check()
100 : : {
101 : 1980 : int fd = ::open(filename.c_str(), O_RDONLY|O_BINARY);
102 : :
103 [ - + ]: 1980 : if (fd < 0) {
104 : 0 : string msg = filename;
105 : 0 : msg += ": Failed to open chert version file for reading";
106 : 0 : throw Xapian::DatabaseOpeningError(msg, errno);
107 : : }
108 : :
109 : : // Try to read an extra byte so we know if the file is too long.
110 : : char buf[VERSIONFILE_SIZE + 1];
111 : : size_t size;
112 : : try {
113 : 1980 : size = io_read(fd, buf, VERSIONFILE_SIZE + 1, 0);
114 : 0 : } catch (...) {
115 : 0 : (void)close(fd);
116 : 0 : throw;
117 : : }
118 : 1980 : (void)close(fd);
119 : :
120 [ - + ]: 1980 : if (size != VERSIONFILE_SIZE) {
121 : : CompileTimeAssert(VERSIONFILE_SIZE == VERSIONFILE_SIZE_LITERAL);
122 : 0 : string msg = filename;
123 : : msg += ": Chert version file should be "
124 : 0 : STRINGIZE(VERSIONFILE_SIZE_LITERAL)" bytes, actually ";
125 : 0 : msg += str(size);
126 : 0 : throw Xapian::DatabaseCorruptError(msg);
127 : : }
128 : :
129 [ - + ]: 1980 : if (memcmp(buf, MAGIC_STRING, MAGIC_LEN) != 0) {
130 : 0 : string msg = filename;
131 : 0 : msg += ": Chert version file doesn't contain the right magic string";
132 : 0 : throw Xapian::DatabaseCorruptError(msg);
133 : : }
134 : :
135 : : const unsigned char *v;
136 : 1980 : v = reinterpret_cast<const unsigned char *>(buf) + MAGIC_LEN;
137 : 1980 : unsigned int version = v[0] | (v[1] << 8) | (v[2] << 16) | (v[3] << 24);
138 [ - + ]: 1980 : if (version != CHERT_VERSION) {
139 : 0 : string msg = filename;
140 : 0 : msg += ": Chert version file is version ";
141 : 0 : msg += str(version);
142 : 0 : msg += " but I only understand "STRINGIZE(CHERT_VERSION);
143 : 0 : throw Xapian::DatabaseVersionError(msg);
144 : : }
145 : :
146 : 1980 : memcpy((void*)uuid, buf + MAGIC_LEN + 4, 16);
147 : 1980 : }
|