1 /*
2    Copyright 2013-2015 Skytechnology sp. z o.o.
3 
4    This file is part of LizardFS.
5 
6    LizardFS is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation, version 3.
9 
10    LizardFS is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with LizardFS. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "common/platform.h"
20 #include "chunkserver/chunk_filename_parser.h"
21 
22 #include "chunkserver/chunk_format.h"
23 #include "common/chunk_part_type.h"
24 #include "common/goal.h"
25 #include "common/parser.h"
26 #include "common/slice_traits.h"
27 
ChunkFilenameParser(const std::string & filename)28 ChunkFilenameParser::ChunkFilenameParser(const std::string& filename)
29 	: Parser(filename),
30 	  chunkFormat_(),
31 	  chunkType_(),
32 	  chunkVersion_(0),
33 	  chunkId_(0) {
34 }
35 
isUpperCaseHexDigit(int c)36 static int isUpperCaseHexDigit(int c) {
37 	return isdigit(c) || (isxdigit(c) && isupper(c));
38 }
39 
parseECChunkType()40 ChunkFilenameParser::Status ChunkFilenameParser::parseECChunkType() {
41 	int data_part_count, parity_part_count, part_index;
42 
43 	if (consume("0") == Parser::OK) {
44 		return ERROR_INVALID_FILENAME;
45 	}
46 	if (consume(isdigit) == Parser::OK) {
47 		part_index = getDecValue<int>() - 1;
48 	} else {
49 		return ERROR_INVALID_FILENAME;
50 	}
51 
52 	if (consume("_of_") != Parser::OK) {
53 		return ERROR_INVALID_FILENAME;
54 	}
55 
56 	if (consume("0") == Parser::OK) {
57 		return ERROR_INVALID_FILENAME;
58 	}
59 	if (consume(isdigit) == Parser::OK) {
60 		data_part_count = getDecValue<int>();
61 		if (data_part_count < slice_traits::ec::kMinDataCount ||
62 		    data_part_count > slice_traits::ec::kMaxDataCount) {
63 			return ERROR_INVALID_FILENAME;
64 		}
65 	} else {
66 		return ERROR_INVALID_FILENAME;
67 	}
68 
69 	if (consume("_") != Parser::OK) {
70 		return ERROR_INVALID_FILENAME;
71 	}
72 
73 	if (consume("0") == Parser::OK) {
74 		return ERROR_INVALID_FILENAME;
75 	}
76 	if (consume(isdigit) == Parser::OK) {
77 		parity_part_count = getDecValue<int>();
78 		if (parity_part_count < slice_traits::ec::kMinParityCount ||
79 		    parity_part_count > slice_traits::ec::kMaxParityCount) {
80 			return ERROR_INVALID_FILENAME;
81 		}
82 	} else {
83 		return ERROR_INVALID_FILENAME;
84 	}
85 
86 	if (consume("_") != Parser::OK) {
87 		return ERROR_INVALID_FILENAME;
88 	}
89 
90 	if (part_index >= (data_part_count + parity_part_count)) {
91 		return ERROR_INVALID_FILENAME;
92 	}
93 
94 	chunkType_ = slice_traits::ec::ChunkPartType(data_part_count, parity_part_count, part_index);
95 	return ChunkFilenameParser::OK;
96 }
97 
parseXorChunkType()98 ChunkFilenameParser::Status ChunkFilenameParser::parseXorChunkType() {
99 	int xor_level, xor_part;
100 
101 	bool is_parity = (consume("parity_of_") == Parser::OK);
102 	if (is_parity) {
103 		if (consume("0") == Parser::OK) {
104 			return ERROR_INVALID_FILENAME;
105 		}
106 		if (consume(isdigit) == Parser::OK) {
107 			if (getLastConsumedCharacterCount() > 2) {
108 				return ERROR_INVALID_FILENAME;
109 			}
110 			xor_level = getDecValue<int>();
111 		} else {
112 			return ERROR_INVALID_FILENAME;
113 		}
114 		if (xor_level < slice_traits::xors::kMinXorLevel ||
115 		    xor_level > slice_traits::xors::kMaxXorLevel) {
116 			return ERROR_INVALID_FILENAME;
117 		}
118 		if (consume("_") != Parser::OK) {
119 			return ERROR_INVALID_FILENAME;
120 		}
121 		chunkType_ =
122 		    slice_traits::xors::ChunkPartType(xor_level, slice_traits::xors::kXorParityPart);
123 		return ChunkFilenameParser::OK;
124 	}
125 
126 	if (consume("0") == Parser::OK) {
127 		return ERROR_INVALID_FILENAME;
128 	}
129 	if (consume(isdigit) == Parser::OK) {
130 		if (getLastConsumedCharacterCount() > 2) {
131 			return ERROR_INVALID_FILENAME;
132 		}
133 		xor_part = getDecValue<int>();
134 	} else {
135 		return ERROR_INVALID_FILENAME;
136 	}
137 	if (xor_part < 1) {
138 		return ERROR_INVALID_FILENAME;
139 	}
140 	if (consume("_of_") != Parser::OK) {
141 		return ERROR_INVALID_FILENAME;
142 	}
143 	if (consume("0") == Parser::OK) {
144 		return ERROR_INVALID_FILENAME;
145 	}
146 	if (consume(isdigit) == Parser::OK) {
147 		if (getLastConsumedCharacterCount() > 2) {
148 			return ERROR_INVALID_FILENAME;
149 		}
150 		xor_level = getDecValue<int>();
151 	} else {
152 		return ERROR_INVALID_FILENAME;
153 	}
154 	if (xor_level < slice_traits::xors::kMinXorLevel ||
155 	    xor_level > slice_traits::xors::kMaxXorLevel || xor_part > xor_level) {
156 		return ERROR_INVALID_FILENAME;
157 	}
158 	if (consume("_") != Parser::OK) {
159 		return ERROR_INVALID_FILENAME;
160 	}
161 	chunkType_ = slice_traits::xors::ChunkPartType(xor_level, xor_part);
162 	return ChunkFilenameParser::OK;
163 }
164 
parseChunkType()165 ChunkFilenameParser::Status ChunkFilenameParser::parseChunkType() try {
166 	bool is_ec = (consume("ec_") == Parser::OK);
167 	if (is_ec) {
168 		return parseECChunkType();
169 	}
170 
171 	bool is_xor_type = (consume("xor_") == Parser::OK);
172 	if (is_xor_type) {
173 		return parseXorChunkType();
174 	}
175 
176 	chunkType_ = slice_traits::standard::ChunkPartType();
177 	return ChunkFilenameParser::OK;
178 } catch (const std:: invalid_argument &e) {
179 	return ChunkFilenameParser::ERROR_INVALID_FILENAME;
180 } catch (const std::out_of_range &e) {
181 	return ChunkFilenameParser::ERROR_INVALID_FILENAME;
182 }
183 
parse()184 ChunkFilenameParser::Status ChunkFilenameParser::parse() try {
185 	chunkFormat_ = ChunkFormat::INTERLEAVED;
186 
187 	if (consume("chunk_") != Parser::OK) {
188 		return ERROR_INVALID_FILENAME;
189 	}
190 
191 	if (parseChunkType() != ChunkFilenameParser::OK) {
192 		return ERROR_INVALID_FILENAME;
193 	}
194 
195 	if (consume(isUpperCaseHexDigit) == Parser::OK) {
196 		if (getLastConsumedCharacterCount() != kChunkIdStringSize) {
197 			return ERROR_INVALID_FILENAME;
198 		}
199 		chunkId_ = getHexValue<uint64_t>();
200 	} else {
201 		return ERROR_INVALID_FILENAME;
202 	}
203 
204 	if (consume("_") != Parser::OK) {
205 		return ERROR_INVALID_FILENAME;
206 	}
207 
208 	if (consume(isUpperCaseHexDigit) == Parser::OK) {
209 		if (getLastConsumedCharacterCount() != kChunkVersionStringSize) {
210 			return ERROR_INVALID_FILENAME;
211 		}
212 		chunkVersion_ = getHexValue<uint32_t>();
213 	} else {
214 		return ERROR_INVALID_FILENAME;
215 	}
216 
217 	if (consume(".liz") != Parser::OK) {
218 		if (consume(".mfs") == Parser::OK) {
219 			chunkFormat_ = ChunkFormat::MOOSEFS;
220 		} else {
221 			return ERROR_INVALID_FILENAME;
222 		}
223 	}
224 
225 	if (consume(1) == Parser::OK) {
226 		// trailing characters
227 		return ERROR_INVALID_FILENAME;
228 	}
229 
230 	return OK;
231 } catch (const std:: invalid_argument &e) {
232 	return ChunkFilenameParser::ERROR_INVALID_FILENAME;
233 } catch (const std::out_of_range &e) {
234 	return ChunkFilenameParser::ERROR_INVALID_FILENAME;
235 }
236 
chunkFormat() const237 ChunkFormat ChunkFilenameParser::chunkFormat() const {
238 	return chunkFormat_;
239 }
240 
chunkType() const241 ChunkPartType ChunkFilenameParser::chunkType() const {
242 	return chunkType_;
243 }
244 
chunkVersion() const245 uint32_t ChunkFilenameParser::chunkVersion() const {
246 	return chunkVersion_;
247 }
248 
chunkId() const249 uint64_t ChunkFilenameParser::chunkId() const {
250 	return chunkId_;
251 }
252