mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
dbaf95c724
This change should make it less confusing where utility code comes from. Having it in two places made sense when we were debating whether to publish Sandbox2 separately, but not any longer. Follow-up changes will move `sandbox2/util.h` and rename the remaining `sandbox2/util` folder. PiperOrigin-RevId: 351601640 Change-Id: I6256845261f610e590c25e2c59851cc51da2d778
152 lines
4.1 KiB
C++
152 lines
4.1 KiB
C++
// Copyright 2019 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "sandboxed_api/util/path.h"
|
|
|
|
#include "absl/strings/match.h"
|
|
#include "absl/strings/str_cat.h"
|
|
#include "absl/strings/strip.h"
|
|
|
|
namespace sapi::file {
|
|
namespace internal {
|
|
|
|
constexpr char kPathSeparator[] = "/";
|
|
|
|
std::string JoinPathImpl(std::initializer_list<absl::string_view> paths) {
|
|
std::string result;
|
|
for (const auto& path : paths) {
|
|
if (path.empty()) {
|
|
continue;
|
|
}
|
|
if (result.empty()) {
|
|
absl::StrAppend(&result, path);
|
|
continue;
|
|
}
|
|
const auto comp = absl::StripPrefix(path, kPathSeparator);
|
|
if (absl::EndsWith(result, kPathSeparator)) {
|
|
absl::StrAppend(&result, comp);
|
|
} else {
|
|
absl::StrAppend(&result, kPathSeparator, comp);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
} // namespace internal
|
|
|
|
bool IsAbsolutePath(absl::string_view path) {
|
|
return !path.empty() && path[0] == '/';
|
|
}
|
|
|
|
std::pair<absl::string_view, absl::string_view> SplitPath(
|
|
absl::string_view path) {
|
|
const auto pos = path.find_last_of('/');
|
|
|
|
// Handle the case with no '/' in 'path'.
|
|
if (pos == absl::string_view::npos) {
|
|
return {path.substr(0, 0), path};
|
|
}
|
|
|
|
// Handle the case with a single leading '/' in 'path'.
|
|
if (pos == 0) {
|
|
return {path.substr(0, 1), absl::ClippedSubstr(path, 1)};
|
|
}
|
|
return {path.substr(0, pos), absl::ClippedSubstr(path, pos + 1)};
|
|
}
|
|
|
|
std::string CleanPath(const absl::string_view unclean_path) {
|
|
auto path = std::string(unclean_path);
|
|
const char* src = path.c_str();
|
|
std::string::iterator dst = path.begin();
|
|
|
|
// Check for absolute path and determine initial backtrack limit.
|
|
const bool is_absolute_path = *src == '/';
|
|
if (is_absolute_path) {
|
|
*dst++ = *src++;
|
|
while (*src == '/') {
|
|
++src;
|
|
}
|
|
}
|
|
std::string::const_iterator backtrack_limit = dst;
|
|
|
|
// Process all parts
|
|
while (*src) {
|
|
bool parsed = false;
|
|
|
|
if (src[0] == '.') {
|
|
// 1dot ".<whateverisnext>", check for END or SEP.
|
|
if (src[1] == '/' || !src[1]) {
|
|
if (*++src) {
|
|
++src;
|
|
}
|
|
parsed = true;
|
|
} else if (src[1] == '.' && (src[2] == '/' || !src[2])) {
|
|
// 2dot END or SEP (".." | "../<whateverisnext>").
|
|
src += 2;
|
|
if (dst != backtrack_limit) {
|
|
// We can backtrack the previous part
|
|
for (--dst; dst != backtrack_limit && dst[-1] != '/'; --dst) {
|
|
// Empty.
|
|
}
|
|
} else if (!is_absolute_path) {
|
|
// Failed to backtrack and we can't skip it either. Rewind and copy.
|
|
src -= 2;
|
|
*dst++ = *src++;
|
|
*dst++ = *src++;
|
|
if (*src) {
|
|
*dst++ = *src;
|
|
}
|
|
// We can never backtrack over a copied "../" part so set new limit.
|
|
backtrack_limit = dst;
|
|
}
|
|
if (*src) {
|
|
++src;
|
|
}
|
|
parsed = true;
|
|
}
|
|
}
|
|
|
|
// If not parsed, copy entire part until the next SEP or EOS.
|
|
if (!parsed) {
|
|
while (*src && *src != '/') {
|
|
*dst++ = *src++;
|
|
}
|
|
if (*src) {
|
|
*dst++ = *src++;
|
|
}
|
|
}
|
|
|
|
// Skip consecutive SEP occurrences.
|
|
while (*src == '/') {
|
|
++src;
|
|
}
|
|
}
|
|
|
|
// Calculate and check the length of the cleaned path.
|
|
int path_length = dst - path.begin();
|
|
if (path_length != 0) {
|
|
// Remove trailing '/' except if it is root path ("/" ==> path_length := 1).
|
|
if (path_length > 1 && path[path_length - 1] == '/') {
|
|
--path_length;
|
|
}
|
|
path.resize(path_length);
|
|
} else {
|
|
// The cleaned path is empty; assign "." as per the spec.
|
|
path.assign(1, '.');
|
|
}
|
|
return path;
|
|
}
|
|
|
|
} // namespace sapi::file
|