| 211 | } |
| 212 | |
| 213 | std::string cmFileAPI::WriteJsonFile( |
| 214 | Json::Value const& value, std::string const& prefix, |
| 215 | std::string (*computeSuffix)(std::string const&)) |
| 216 | { |
| 217 | std::string fileName; |
| 218 | |
| 219 | // Write the json file with a temporary name. |
| 220 | std::string const& tmpFile = this->APIv1 + "/tmp.json"; |
| 221 | cmsys::ofstream ftmp(tmpFile.c_str()); |
| 222 | this->JsonWriter->write(value, &ftmp); |
| 223 | ftmp << "\n"; |
| 224 | ftmp.close(); |
| 225 | if (!ftmp) { |
| 226 | cmSystemTools::RemoveFile(tmpFile); |
| 227 | return fileName; |
| 228 | } |
| 229 | |
| 230 | // Compute the final name for the file. |
| 231 | std::string suffix = computeSuffix(tmpFile); |
| 232 | std::string suffixWithExtension = cmStrCat('-', suffix, ".json"); |
| 233 | fileName = cmStrCat(prefix, suffixWithExtension); |
| 234 | |
| 235 | // Truncate the file name length |
| 236 | // eCryptFS has a maximal file name length recommendation of 140 |
| 237 | size_t const maxFileNameLength = 140; |
| 238 | size_t const fileNameLength = fileName.size(); |
| 239 | if (fileNameLength > maxFileNameLength) { |
| 240 | size_t const newHashLength = 20; |
| 241 | size_t const newSuffixLength = |
| 242 | suffixWithExtension.size() - suffix.size() + newHashLength; |
| 243 | size_t const overLength = |
| 244 | fileNameLength - maxFileNameLength + newSuffixLength; |
| 245 | size_t const startPos = fileNameLength - overLength; |
| 246 | std::string const toBeRemoved = fileName.substr(startPos, overLength); |
| 247 | suffix = cmCryptoHash(cmCryptoHash::AlgoSHA256) |
| 248 | .HashString(toBeRemoved) |
| 249 | .substr(0, newHashLength); |
| 250 | suffixWithExtension = cmStrCat('-', suffix, ".json"); |
| 251 | fileName.replace(startPos, overLength, suffixWithExtension); |
| 252 | } |
| 253 | |
| 254 | // Create the destination. |
| 255 | std::string file = this->APIv1 + "/reply"; |
| 256 | cmSystemTools::MakeDirectory(file); |
| 257 | file += "/"; |
| 258 | file += fileName; |
| 259 | |
| 260 | // If the final name already exists then assume it has proper content. |
| 261 | // Otherwise, atomically place the reply file at its final name |
| 262 | if (cmSystemTools::FileExists(file, true) || |
| 263 | !cmSystemTools::RenameFile(tmpFile, file)) { |
| 264 | cmSystemTools::RemoveFile(tmpFile); |
| 265 | } |
| 266 | |
| 267 | // Record this among files we have just written. |
| 268 | this->ReplyFiles.insert(fileName); |
| 269 | |
| 270 | return fileName; |
no test coverage detected