1 // Copyright (C) 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef ICING_FILE_VERSION_UTIL_H_
16 #define ICING_FILE_VERSION_UTIL_H_
17
18 #include <cstdint>
19 #include <memory>
20 #include <string>
21 #include <string_view>
22
23 #include "icing/text_classifier/lib3/utils/base/status.h"
24 #include "icing/text_classifier/lib3/utils/base/statusor.h"
25 #include "icing/absl_ports/str_cat.h"
26 #include "icing/file/derived-file-util.h"
27 #include "icing/file/filesystem.h"
28 #include "icing/proto/initialize.pb.h"
29
30 namespace icing {
31 namespace lib {
32
33 namespace version_util {
34
35 // - Version 0: Android T base. Can be identified only by flash index magic.
36 // - Version 1: Android U base and M-2023-08.
37 // - Version 2: M-2023-09, M-2023-11, M-2024-01. Schema is compatible with v1.
38 // (There were no M-2023-10, M-2023-12).
39 // - Version 3: M-2024-02. Schema is compatible with v1 and v2.
40 // - Version 4: Android V base. Schema is compatible with v1, v2 and v3.
41 // - Version 5: M-2025-02. Schema is compatible with v1, v2, v3 and v4.
42 inline static constexpr int32_t kVersion = 7;
43 inline static constexpr int32_t kVersionOne = 1;
44 inline static constexpr int32_t kVersionTwo = 2;
45 inline static constexpr int32_t kVersionThree = 3;
46 inline static constexpr int32_t kVersionFour = 4;
47 inline static constexpr int32_t kVersionFive = 5;
48
49 // Version at which v2 version file is introduced.
50 inline static constexpr int32_t kFirstV2Version = kVersionFour;
51
52 // Version at which the database field is introduced to the schema proto.
53 inline static constexpr int32_t kSchemaDatabaseVersion = kVersionFive;
54
55 inline static constexpr int kVersionZeroFlashIndexMagic = 0x6dfba6ae;
56
57 inline static constexpr std::string_view kVersionFilenameV1 = "version";
58 inline static constexpr std::string_view kVersionFilenameV2 = "version2";
59
60 struct VersionInfo {
61 int32_t version;
62 int32_t max_version;
63
VersionInfoVersionInfo64 explicit VersionInfo(int32_t version_in, int32_t max_version_in)
65 : version(version_in), max_version(max_version_in) {}
66
IsValidVersionInfo67 bool IsValid() const { return version >= 0 && max_version >= 0; }
68
69 bool operator==(const VersionInfo& other) const {
70 return version == other.version && max_version == other.max_version;
71 }
72 } __attribute__((packed));
73 static_assert(sizeof(VersionInfo) == 8, "");
74
75 enum class StateChange {
76 kUndetermined,
77 kCompatible,
78 kRollForward,
79 kRollBack,
80 kUpgrade,
81 kVersionZeroUpgrade,
82 kVersionZeroRollForward,
83 };
84
85 // There are two icing version files:
86 // 1. V1 version file contains version and max_version info of the existing
87 // data.
88 // 2. V2 version file writes the version information using
89 // FileBackedProto<IcingSearchEngineVersionProto>. This contains information
90 // about the version's enabled trunk stable features in addition to the
91 // version numbers written for V1.
92 //
93 // Both version files must be written to maintain backwards compatibility.
MakeVersionFilePath(std::string_view version_file_dir,std::string_view version_file_name)94 inline std::string MakeVersionFilePath(std::string_view version_file_dir,
95 std::string_view version_file_name) {
96 return absl_ports::StrCat(version_file_dir, "/", version_file_name);
97 }
98
99 // Returns a VersionInfo from a given IcingSearchEngineVersionProto.
GetVersionInfoFromProto(const IcingSearchEngineVersionProto & version_proto)100 inline VersionInfo GetVersionInfoFromProto(
101 const IcingSearchEngineVersionProto& version_proto) {
102 return VersionInfo(version_proto.version(), version_proto.max_version());
103 }
104
105 // Reads the IcingSearchEngineVersionProto from the version files of the
106 // existing data.
107 //
108 // This method reads both the v1 and v2 version files, and returns the v1
109 // version numbers in the absence of the v2 version file. If there is a mismatch
110 // between the v1 and v2 version numbers, or if the state is invalid (e.g. flash
111 // index header file is missing), then an invalid VersionInfo is returned.
112 //
113 // RETURNS:
114 // - Existing data's IcingSearchEngineVersionProto on success
115 // - INTERNAL_ERROR on I/O errors
116 libtextclassifier3::StatusOr<IcingSearchEngineVersionProto> ReadVersion(
117 const Filesystem& filesystem, const std::string& version_file_dir,
118 const std::string& index_base_dir);
119
120 // Writes the v1 version file. V1 version file is written for all versions and
121 // contains only Icing's VersionInfo (version number and max_version)
122 //
123 // RETURNS:
124 // - OK on success
125 // - INTERNAL_ERROR on I/O errors
126 libtextclassifier3::Status WriteV1Version(const Filesystem& filesystem,
127 const std::string& version_file_dir,
128 const VersionInfo& version_info);
129
130 // Writes the v2 version file. V2 version file writes the version information
131 // using FileBackedProto<IcingSearchEngineVersionProto>.
132 //
133 // REQUIRES: version_proto.version >= kFirstV2Version. We implement v2 version
134 // checking in kFirstV2Version, so callers will always use a version # greater
135 // than this.
136 //
137 // RETURNS:
138 // - OK on success
139 // - INTERNAL_ERROR on I/O errors
140 libtextclassifier3::Status WriteV2Version(
141 const Filesystem& filesystem, const std::string& version_file_dir,
142 std::unique_ptr<IcingSearchEngineVersionProto> version_proto);
143
144 // Deletes Icing's version files from version_file_dir.
145 //
146 // Returns:
147 // - OK on success
148 // - INTERNAL_ERROR on I/O error
149 libtextclassifier3::Status DiscardVersionFiles(
150 const Filesystem& filesystem, std::string_view version_file_dir);
151
152 // Determines the change state between the existing data version and the current
153 // code version.
154 //
155 // REQUIRES: curr_version > 0. We implement version checking in version 1, so
156 // the callers (except unit tests) will always use a version # greater than 0.
157 //
158 // RETURNS: StateChange
159 StateChange GetVersionStateChange(const VersionInfo& existing_version_info,
160 int32_t curr_version = kVersion);
161
162 // Determines the derived files that need to be rebuilt between Icing's existing
163 // data based on previous data version and enabled features, and the current
164 // code version and enabled features.
165 //
166 // REQUIRES: curr_version >= kFirstV2Version. We implement v2 version checking
167 // in kFirstV2Version, so callers will always use a version # greater than this.
168 //
169 // RETURNS: derived_file_util::DerivedFilesRebuildInfo
170 derived_file_util::DerivedFilesRebuildInfo CalculateRequiredDerivedFilesRebuild(
171 const IcingSearchEngineVersionProto& prev_version_proto,
172 const IcingSearchEngineVersionProto& curr_version_proto);
173
174 // Determines whether Icing should rebuild all derived files.
175 // Sometimes it is not required to rebuild derived files when
176 // roll-forward/upgrading. This function "encodes" upgrade paths and checks if
177 // the roll-forward/upgrading requires derived files to be rebuilt or not.
178 //
179 // REQUIRES: curr_version > 0. We implement version checking in version 1, so
180 // the callers (except unit tests) will always use a version # greater than 0.
181 bool ShouldRebuildDerivedFiles(const VersionInfo& existing_version_info,
182 int32_t curr_version = kVersion);
183
184 // Returns whether the schema database migration is required.
185 //
186 // This is true if the previous version is less than the version at which the
187 // database field is introduced, or if the schema database feature was
188 // not enabled in the previous version.
189 bool SchemaDatabaseMigrationRequired(
190 const IcingSearchEngineVersionProto& prev_version_proto);
191
192 // Returns the derived files rebuilds required for a given feature.
193 derived_file_util::DerivedFilesRebuildInfo GetFeatureDerivedFilesRebuildInfo(
194 IcingSearchEngineFeatureInfoProto::FlaggedFeatureType feature);
195
196 // Constructs the IcingSearchEngineFeatureInfoProto for a given feature.
197 IcingSearchEngineFeatureInfoProto GetFeatureInfoProto(
198 IcingSearchEngineFeatureInfoProto::FlaggedFeatureType feature);
199
200 // Populates the enabled_features field for an IcingSearchEngineFeatureInfoProto
201 // based on icing's initialization flag options.
202 //
203 // All enabled features are converted into an IcingSearchEngineFeatureInfoProto
204 // and returned in IcingSearchEngineVersionProto::enabled_features. A conversion
205 // should be added for each trunk stable feature flag defined in
206 // IcingSearchEngineOptions.
207 void AddEnabledFeatures(const IcingSearchEngineOptions& options,
208 IcingSearchEngineVersionProto* version_proto);
209
210 } // namespace version_util
211
212 } // namespace lib
213 } // namespace icing
214
215 #endif // ICING_FILE_VERSION_UTIL_H_
216