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