Branch data Line data Source code
1 : : /** @file flint_version.cc
2 : : * @brief FlintVersion 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 "flint_version.h"
29 : : #include "io_utils.h"
30 : : #include "str.h"
31 : : #include "stringutils.h" // For STRINGIZE() and CONST_STRLEN().
32 : : #include "utils.h"
33 : :
34 : : #ifdef __WIN32__
35 : : # include "msvc_posix_wrapper.h"
36 : : #endif
37 : :
38 : : #include <cstdio> // For rename().
39 : : #include <cstring> // For memcmp().
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 FLINT_VERSION 200709120
48 : : // 200709120 1.0.3 Database::get_metadata(), WritableDatabase::set_metadata().
49 : : // Kill the unused "has_termfreqs" flag in the termlist table.
50 : : // 200706140 1.0.2 Optional value and position tables.
51 : : // 200704230 1.0.0 Use zlib compression of tags for record and termlist tables.
52 : : // 200611200 N/A Fixed occasional, architecture-dependent surplus bits in
53 : : // interpolative coding; "flicklock" -> "flintlock".
54 : : // 200506110 0.9.2 Fixed interpolative coding to work(!)
55 : : // 200505310 0.9.1 Interpolative coding for position lists.
56 : : // 200505280 N/A Total doclen and last docid entry moved to postlist table.
57 : : // 200505270 N/A First dated version.
58 : :
59 : : #define MAGIC_STRING "IAmFlint"
60 : :
61 : : #define MAGIC_LEN CONST_STRLEN(MAGIC_STRING)
62 : : #define VERSIONFILE_SIZE (MAGIC_LEN + 4)
63 : :
64 : 318 : void FlintVersion::create()
65 : : {
66 : 318 : char buf[VERSIONFILE_SIZE] = MAGIC_STRING;
67 : 318 : unsigned char *v = reinterpret_cast<unsigned char *>(buf) + MAGIC_LEN;
68 : 318 : v[0] = static_cast<unsigned char>(FLINT_VERSION & 0xff);
69 : 318 : v[1] = static_cast<unsigned char>((FLINT_VERSION >> 8) & 0xff);
70 : 318 : v[2] = static_cast<unsigned char>((FLINT_VERSION >> 16) & 0xff);
71 : 318 : v[3] = static_cast<unsigned char>((FLINT_VERSION >> 24) & 0xff);
72 : :
73 : 318 : int fd = ::open(filename.c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
74 : :
75 [ - + ]: 318 : if (fd < 0) {
76 : 0 : string msg("Failed to create flint version file: ");
77 : 0 : msg += filename;
78 : 0 : throw Xapian::DatabaseOpeningError(msg, errno);
79 : : }
80 : :
81 : : try {
82 : 318 : io_write(fd, buf, VERSIONFILE_SIZE);
83 : 0 : } catch (...) {
84 : 0 : (void)close(fd);
85 : 0 : throw;
86 : : }
87 : :
88 [ - + ]: 318 : if (close(fd) != 0) {
89 : 0 : string msg("Failed to create flint version file: ");
90 : 0 : msg += filename;
91 : 0 : throw Xapian::DatabaseOpeningError(msg, errno);
92 : : }
93 : :
94 : 318 : uuid_clear(uuid);
95 : 318 : ensure_uuid();
96 : 318 : }
97 : :
98 : 1901 : void FlintVersion::read_and_check(bool readonly)
99 : : {
100 : 1901 : int fd = ::open(filename.c_str(), O_RDONLY|O_BINARY);
101 : :
102 [ + + ]: 1901 : if (fd < 0) {
103 : 3 : string msg("Failed to open flint version file for reading: ");
104 : 3 : msg += filename;
105 : 6 : throw Xapian::DatabaseOpeningError(msg, errno);
106 : : }
107 : :
108 : : // Try to read an extra byte so we know if the file is too long.
109 : : char buf[VERSIONFILE_SIZE + 1];
110 : : size_t size;
111 : : try {
112 : 1898 : size = io_read(fd, buf, VERSIONFILE_SIZE + 1, 0);
113 : 0 : } catch (...) {
114 : 0 : (void)close(fd);
115 : 0 : throw;
116 : : }
117 : 1898 : (void)close(fd);
118 : :
119 [ - + ]: 1898 : if (size != VERSIONFILE_SIZE) {
120 : 0 : string msg("Flint version file ");
121 : 0 : msg += filename;
122 : 0 : msg += " should be "STRINGIZE(VERSIONFILE_SIZE)" bytes, actually ";
123 : 0 : msg += str(size);
124 : 0 : throw Xapian::DatabaseCorruptError(msg);
125 : : }
126 : :
127 [ - + ]: 1898 : if (memcmp(buf, MAGIC_STRING, MAGIC_LEN) != 0) {
128 : 0 : string msg("Flint version file doesn't contain the right magic string: ");
129 : 0 : msg += filename;
130 : 0 : throw Xapian::DatabaseCorruptError(msg);
131 : : }
132 : :
133 : : const unsigned char *v;
134 : 1898 : v = reinterpret_cast<const unsigned char *>(buf) + MAGIC_LEN;
135 : 1898 : unsigned int version = v[0] | (v[1] << 8) | (v[2] << 16) | (v[3] << 24);
136 [ + + ][ + + ]: 1898 : if (version >= 200704230 && version < 200709120) {
137 [ + + ]: 4 : if (readonly) return;
138 : : // Upgrade the database to the current version since any changes we
139 : : // make won't be compatible with older versions of Xapian.
140 : 2 : string filename_save = filename;
141 : 2 : filename += ".tmp";
142 : 2 : create();
143 : : int result;
144 : : #ifdef __WIN32__
145 : : result = msvc_posix_rename(filename.c_str(), filename_save.c_str());
146 : : #else
147 : 2 : result = rename(filename.c_str(), filename_save.c_str());
148 : : #endif
149 : 2 : filename = filename_save;
150 [ - + ]: 2 : if (result == -1) {
151 : 0 : string msg("Failed to update flint version file: ");
152 : 0 : msg += filename;
153 : 0 : throw Xapian::DatabaseOpeningError(msg);
154 : : }
155 : 2 : return;
156 : : }
157 [ + + ]: 1894 : if (version != FLINT_VERSION) {
158 : 3 : string msg("Flint version file ");
159 : 3 : msg += filename;
160 : 3 : msg += " is version ";
161 : 3 : msg += str(version);
162 : 3 : msg += " but I only understand "STRINGIZE(FLINT_VERSION);
163 : 6 : throw Xapian::DatabaseVersionError(msg);
164 : : }
165 : :
166 : 1891 : string f = filename;
167 : 1891 : f.resize(f.size() - CONST_STRLEN("iamflint"));
168 : 1891 : f += "uuid";
169 : 1891 : fd = ::open(f.c_str(), O_RDONLY|O_BINARY);
170 : :
171 [ - + ]: 1891 : if (fd < 0) {
172 : 0 : uuid_clear(uuid);
173 : : return;
174 : : }
175 : :
176 : : try {
177 : 1891 : (void)io_read(fd, reinterpret_cast<char*>(uuid), 16, 16);
178 : 0 : } catch (...) {
179 : 0 : uuid_clear(uuid);
180 : 0 : (void)close(fd);
181 : 0 : throw;
182 : : }
183 : 1895 : (void)close(fd);
184 : : }
185 : :
186 : : void
187 : 1713 : FlintVersion::ensure_uuid() const
188 : : {
189 [ + + ]: 1713 : if (uuid_is_null(uuid)) {
190 : 318 : string f = filename;
191 : 318 : f.resize(f.size() - CONST_STRLEN("iamflint"));
192 : 318 : f += "uuid";
193 : 318 : int fd = ::open(f.c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
194 : :
195 : : // Might be read-only, so don't error, but instead fall back to
196 : : // using the mtime of the version file.
197 [ - + ]: 318 : if (fd < 0) {
198 : : struct stat statbuf;
199 [ # # ]: 0 : if (stat(filename, &statbuf) != 0) {
200 : 0 : throw Xapian::DatabaseError("Couldn't stat " + filename, errno);
201 : : }
202 : 0 : unsigned long mtime = statbuf.st_mtime;
203 : : // This isn't a validly generated UUID, but it'll do for the
204 : : // purpose of identifying a database master for replication
205 : : // while we transition to "real" UUIDs.
206 : 0 : unsigned char *v = reinterpret_cast<unsigned char *>(uuid);
207 : 0 : v[0] = static_cast<unsigned char>(mtime & 0xff);
208 : 0 : v[1] = static_cast<unsigned char>((mtime >> 8) & 0xff);
209 : 0 : v[2] = static_cast<unsigned char>((mtime >> 16) & 0xff);
210 : 0 : v[3] = static_cast<unsigned char>((mtime >> 24) & 0xff);
211 : : return;
212 : : }
213 : :
214 : 318 : uuid_generate(uuid);
215 : : try {
216 : 318 : io_write(fd, reinterpret_cast<const char*>(uuid), 16);
217 : 0 : } catch (...) {
218 : 0 : (void)close(fd);
219 : 0 : throw;
220 : : }
221 : :
222 [ - + ]: 318 : if (close(fd) != 0) {
223 : 0 : string msg("Failed to create flint uuid file: ");
224 : 0 : msg += f;
225 : 318 : throw Xapian::DatabaseError(msg, errno);
226 : 1713 : }
227 : : }
228 : : }
|