Branch data Line data Source code
1 : : /** @file brass_version.cc
2 : : * @brief BrassVersion class
3 : : */
4 : : /* Copyright (C) 2006,2007,2008,2009,2010 Olly Betts
5 : : * Copyright (C) 2011 Dan Colish
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 "brass_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 BRASS_VERSION 201103110
48 : : // 201103110 1.2.5 Bump for new max changesets dbstats
49 : : // 200912150 1.1.4 Brass debuts.
50 : :
51 : : #define MAGIC_STRING "IAmBrass"
52 : :
53 : : #define MAGIC_LEN CONST_STRLEN(MAGIC_STRING)
54 : : // 4 for the version number; 16 for the UUID.
55 : : #define VERSIONFILE_SIZE (MAGIC_LEN + 4 + 16)
56 : :
57 : : // Literal version of VERSIONFILE_SIZE, used for error message. This needs
58 : : // to be updated by hand should VERSIONFILE_SIZE change, but that rarely
59 : : // happens so this isn't an onerous requirement.
60 : : #define VERSIONFILE_SIZE_LITERAL 28
61 : :
62 : : void
63 : 319 : BrassVersion::create()
64 : : {
65 : 319 : char buf[VERSIONFILE_SIZE] = MAGIC_STRING;
66 : 319 : unsigned char *v = reinterpret_cast<unsigned char *>(buf) + MAGIC_LEN;
67 : 319 : v[0] = static_cast<unsigned char>(BRASS_VERSION & 0xff);
68 : 319 : v[1] = static_cast<unsigned char>((BRASS_VERSION >> 8) & 0xff);
69 : 319 : v[2] = static_cast<unsigned char>((BRASS_VERSION >> 16) & 0xff);
70 : 319 : v[3] = static_cast<unsigned char>((BRASS_VERSION >> 24) & 0xff);
71 : :
72 : 319 : uuid_generate(uuid);
73 : 319 : memcpy(buf + MAGIC_LEN + 4, (void*)uuid, 16);
74 : :
75 : 319 : int fd = ::open(filename.c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
76 : :
77 [ - + ]: 319 : if (fd < 0) {
78 : 0 : string msg("Failed to create brass version file: ");
79 : 0 : msg += filename;
80 : 0 : throw Xapian::DatabaseOpeningError(msg, errno);
81 : : }
82 : :
83 : : try {
84 : 319 : io_write(fd, buf, VERSIONFILE_SIZE);
85 : 0 : } catch (...) {
86 : 0 : (void)close(fd);
87 : 0 : throw;
88 : : }
89 : :
90 [ - + ]: 319 : if (close(fd) != 0) {
91 : 0 : string msg("Failed to create brass version file: ");
92 : 0 : msg += filename;
93 : 0 : throw Xapian::DatabaseOpeningError(msg, errno);
94 : : }
95 : 319 : }
96 : :
97 : : void
98 : 1919 : BrassVersion::read_and_check()
99 : : {
100 : 1919 : int fd = ::open(filename.c_str(), O_RDONLY|O_BINARY);
101 : :
102 [ - + ]: 1919 : if (fd < 0) {
103 : 0 : string msg = filename;
104 : 0 : msg += ": Failed to open brass version file for reading";
105 : 0 : 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 : 1919 : size = io_read(fd, buf, VERSIONFILE_SIZE + 1, 0);
113 : 0 : } catch (...) {
114 : 0 : (void)close(fd);
115 : 0 : throw;
116 : : }
117 : 1919 : (void)close(fd);
118 : :
119 [ - + ]: 1919 : if (size != VERSIONFILE_SIZE) {
120 : : CompileTimeAssert(VERSIONFILE_SIZE == VERSIONFILE_SIZE_LITERAL);
121 : 0 : string msg = filename;
122 : : msg += ": Brass version file should be "
123 : 0 : STRINGIZE(VERSIONFILE_SIZE_LITERAL)" bytes, actually ";
124 : 0 : msg += str(size);
125 : 0 : throw Xapian::DatabaseCorruptError(msg);
126 : : }
127 : :
128 [ - + ]: 1919 : if (memcmp(buf, MAGIC_STRING, MAGIC_LEN) != 0) {
129 : 0 : string msg = filename;
130 : 0 : msg += ": Brass version file doesn't contain the right magic string";
131 : 0 : throw Xapian::DatabaseCorruptError(msg);
132 : : }
133 : :
134 : : const unsigned char *v;
135 : 1919 : v = reinterpret_cast<const unsigned char *>(buf) + MAGIC_LEN;
136 : 1919 : unsigned int version = v[0] | (v[1] << 8) | (v[2] << 16) | (v[3] << 24);
137 [ - + ]: 1919 : if (version != BRASS_VERSION) {
138 : 0 : string msg = filename;
139 : 0 : msg += ": Brass version file is version ";
140 : 0 : msg += str(version);
141 : 0 : msg += " but I only understand "STRINGIZE(BRASS_VERSION);
142 : 0 : throw Xapian::DatabaseVersionError(msg);
143 : : }
144 : :
145 : 1919 : memcpy((void*)uuid, buf + MAGIC_LEN + 4, 16);
146 : 1919 : }
|