Commit e05e777d authored by Laszlo Csomor's avatar Laszlo Csomor

Windows: support long paths

Add implementations of open(2), mkdir(2), stat(2),
etc. that support long paths under Windows (paths
longer than MAX_PATH in <windows.h>, which is 260
characters).

The implementations are in a separate namespace
(google::protobuf::internal::win32), so they won't
collide with the standard implementations in
<io.h>, but after importing them with `using` they
can be drop-in replacements.

Fixes https://github.com/bazelbuild/bazel/issues/2634
Fixes https://github.com/google/protobuf/issues/2891
parent 9ab7c73f
......@@ -108,6 +108,7 @@ cc_library(
"src/google/protobuf/stubs/bytestream.cc",
"src/google/protobuf/stubs/common.cc",
"src/google/protobuf/stubs/int128.cc",
"src/google/protobuf/stubs/io_win32.cc",
"src/google/protobuf/stubs/once.cc",
"src/google/protobuf/stubs/status.cc",
"src/google/protobuf/stubs/statusor.cc",
......@@ -159,6 +160,7 @@ cc_library(
"src/google/protobuf/service.cc",
"src/google/protobuf/source_context.pb.cc",
"src/google/protobuf/struct.pb.cc",
"src/google/protobuf/stubs/io_win32.cc",
"src/google/protobuf/stubs/mathlimits.cc",
"src/google/protobuf/stubs/substitute.cc",
"src/google/protobuf/text_format.cc",
......@@ -470,6 +472,7 @@ COMMON_TEST_SRCS = [
# AUTOGEN(common_test_srcs)
"src/google/protobuf/arena_test_util.cc",
"src/google/protobuf/map_test_util.cc",
"src/google/protobuf/stubs/io_win32.cc",
"src/google/protobuf/test_util.cc",
"src/google/protobuf/testing/file.cc",
"src/google/protobuf/testing/googletest.cc",
......@@ -490,6 +493,16 @@ cc_binary(
],
)
cc_test(
name = "win32_test",
srcs = ["src/google/protobuf/stubs/io_win32_unittest.cc"],
deps = [
":protobuf_lite",
"//external:gtest_main",
],
tags = ["manual", "windows"],
)
cc_test(
name = "protobuf_test",
srcs = COMMON_TEST_SRCS + [
......@@ -536,6 +549,7 @@ cc_test(
"src/google/protobuf/stubs/bytestream_unittest.cc",
"src/google/protobuf/stubs/common_unittest.cc",
"src/google/protobuf/stubs/int128_unittest.cc",
"src/google/protobuf/stubs/io_win32_unittest.cc",
"src/google/protobuf/stubs/once_unittest.cc",
"src/google/protobuf/stubs/status_test.cc",
"src/google/protobuf/stubs/statusor_test.cc",
......
......@@ -99,6 +99,7 @@ copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\casts.h" includ
copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\common.h" include\google\protobuf\stubs\common.h
copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\fastmem.h" include\google\protobuf\stubs\fastmem.h
copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\hash.h" include\google\protobuf\stubs\hash.h
copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\io_win32.h" include\google\protobuf\stubs\io_win32.h
copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\logging.h" include\google\protobuf\stubs\logging.h
copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\macros.h" include\google\protobuf\stubs\macros.h
copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\mutex.h" include\google\protobuf\stubs\mutex.h
......
......@@ -13,6 +13,7 @@ set(libprotobuf_lite_files
${protobuf_source_dir}/src/google/protobuf/stubs/bytestream.cc
${protobuf_source_dir}/src/google/protobuf/stubs/common.cc
${protobuf_source_dir}/src/google/protobuf/stubs/int128.cc
${protobuf_source_dir}/src/google/protobuf/stubs/io_win32.cc
${protobuf_source_dir}/src/google/protobuf/stubs/once.cc
${protobuf_source_dir}/src/google/protobuf/stubs/status.cc
${protobuf_source_dir}/src/google/protobuf/stubs/statusor.cc
......
......@@ -93,6 +93,7 @@ set(libprotoc_files
${protobuf_source_dir}/src/google/protobuf/compiler/ruby/ruby_generator.cc
${protobuf_source_dir}/src/google/protobuf/compiler/subprocess.cc
${protobuf_source_dir}/src/google/protobuf/compiler/zip_writer.cc
${protobuf_source_dir}/src/google/protobuf/stubs/io_win32.cc
)
set(libprotoc_headers
......
......@@ -160,6 +160,8 @@ set(tests_files
${protobuf_source_dir}/src/google/protobuf/stubs/bytestream_unittest.cc
${protobuf_source_dir}/src/google/protobuf/stubs/common_unittest.cc
${protobuf_source_dir}/src/google/protobuf/stubs/int128_unittest.cc
${protobuf_source_dir}/src/google/protobuf/stubs/io_win32.cc
${protobuf_source_dir}/src/google/protobuf/stubs/io_win32_unittest.cc
${protobuf_source_dir}/src/google/protobuf/stubs/once_unittest.cc
${protobuf_source_dir}/src/google/protobuf/stubs/status_test.cc
${protobuf_source_dir}/src/google/protobuf/stubs/statusor_test.cc
......@@ -198,6 +200,7 @@ target_link_libraries(tests libprotoc libprotobuf gmock_main)
set(test_plugin_files
${protobuf_source_dir}/src/google/protobuf/compiler/mock_code_generator.cc
${protobuf_source_dir}/src/google/protobuf/stubs/io_win32.cc
${protobuf_source_dir}/src/google/protobuf/testing/file.cc
${protobuf_source_dir}/src/google/protobuf/testing/file.h
${protobuf_source_dir}/src/google/protobuf/compiler/test_plugin.cc
......
......@@ -198,6 +198,8 @@ libprotobuf_lite_la_SOURCES = \
google/protobuf/stubs/hash.h \
google/protobuf/stubs/int128.cc \
google/protobuf/stubs/int128.h \
google/protobuf/stubs/io_win32.cc \
google/protobuf/stubs/io_win32.h \
google/protobuf/stubs/map_util.h \
google/protobuf/stubs/mathutil.h \
google/protobuf/stubs/once.cc \
......@@ -761,6 +763,7 @@ protobuf_test_SOURCES = \
google/protobuf/stubs/bytestream_unittest.cc \
google/protobuf/stubs/common_unittest.cc \
google/protobuf/stubs/int128_unittest.cc \
google/protobuf/stubs/io_win32_unittest.cc \
google/protobuf/stubs/once_unittest.cc \
google/protobuf/stubs/statusor_test.cc \
google/protobuf/stubs/status_test.cc \
......
......@@ -45,10 +45,7 @@
#endif
#include <sys/stat.h>
#include <fcntl.h>
#ifdef _MSC_VER
#include <io.h>
#include <direct.h>
#else
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <errno.h>
......@@ -80,6 +77,7 @@
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/io_win32.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
......@@ -91,22 +89,6 @@ namespace google {
namespace protobuf {
namespace compiler {
#if defined(_WIN32)
#define mkdir(name, mode) mkdir(name)
#ifndef W_OK
#define W_OK 02 // not defined by MSVC for whatever reason
#endif
#ifndef F_OK
#define F_OK 00 // not defined by MSVC for whatever reason
#endif
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
#define STDOUT_FILENO 1
#endif
#endif
#ifndef O_BINARY
#ifdef _O_BINARY
#define O_BINARY _O_BINARY
......@@ -118,6 +100,14 @@ namespace compiler {
namespace {
#if defined(_WIN32) && !defined(__CYGWIN__)
static const char* kPathSeparator = ";";
// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
// them like we do below.
using google::protobuf::internal::win32::access;
using google::protobuf::internal::win32::close;
using google::protobuf::internal::win32::mkdir;
using google::protobuf::internal::win32::open;
using google::protobuf::internal::win32::setmode;
using google::protobuf::internal::win32::write;
#else
static const char* kPathSeparator = ":";
#endif
......@@ -141,9 +131,9 @@ static bool IsWindowsAbsolutePath(const string& text) {
void SetFdToTextMode(int fd) {
#ifdef _WIN32
if (_setmode(fd, _O_TEXT) == -1) {
if (setmode(fd, _O_TEXT) == -1) {
// This should never happen, I think.
GOOGLE_LOG(WARNING) << "_setmode(" << fd << ", _O_TEXT): " << strerror(errno);
GOOGLE_LOG(WARNING) << "setmode(" << fd << ", _O_TEXT): " << strerror(errno);
}
#endif
// (Text and binary are the same on non-Windows platforms.)
......@@ -151,9 +141,9 @@ void SetFdToTextMode(int fd) {
void SetFdToBinaryMode(int fd) {
#ifdef _WIN32
if (_setmode(fd, _O_BINARY) == -1) {
if (setmode(fd, _O_BINARY) == -1) {
// This should never happen, I think.
GOOGLE_LOG(WARNING) << "_setmode(" << fd << ", _O_BINARY): " << strerror(errno);
GOOGLE_LOG(WARNING) << "setmode(" << fd << ", _O_BINARY): " << strerror(errno);
}
#endif
// (Text and binary are the same on non-Windows platforms.)
......
......@@ -35,9 +35,7 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef _MSC_VER
#include <io.h>
#else
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <memory>
......@@ -65,27 +63,27 @@
#include <gtest/gtest.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/io_win32.h>
namespace google {
namespace protobuf {
namespace compiler {
#if defined(_WIN32)
// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
// them like we do below.
using google::protobuf::internal::win32::access;
using google::protobuf::internal::win32::dup;
using google::protobuf::internal::win32::dup2;
using google::protobuf::internal::win32::close;
using google::protobuf::internal::win32::open;
using google::protobuf::internal::win32::write;
#endif
// Disable the whole test when we use tcmalloc for "draconian" heap checks, in
// which case tcmalloc will print warnings that fail the plugin tests.
#if !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
#if defined(_WIN32)
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
#define STDOUT_FILENO 1
#endif
#ifndef F_OK
#define F_OK 00 // not defined by MSVC for whatever reason
#endif
#endif
namespace {
bool FileExists(const string& path) {
......
......@@ -33,7 +33,7 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#ifdef _MSC_VER
#include <io.h>
#include <direct.h>
#else
#include <unistd.h>
#endif
......@@ -53,19 +53,21 @@
#include <google/protobuf/compiler/parser.h>
#include <google/protobuf/io/tokenizer.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/io_win32.h>
#include <google/protobuf/stubs/strutil.h>
namespace google {
namespace protobuf {
namespace compiler {
#ifdef _WIN32
#ifndef F_OK
#define F_OK 00 // not defined by MSVC for whatever reason
#endif
#include <ctype.h>
// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
// them like we do below.
using google::protobuf::internal::win32::access;
using google::protobuf::internal::win32::open;
#endif
namespace google {
namespace protobuf {
namespace compiler {
// Returns true if the text looks like a Windows-style absolute path, starting
// with a drive letter. Example: "C:\foo". TODO(kenton): Share this with
// copy in command_line_interface.cc?
......
......@@ -28,9 +28,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifdef _MSC_VER
#include <io.h>
#else
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <climits>
......@@ -49,8 +47,15 @@
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/io_win32.h>
#include <google/protobuf/stubs/strutil.h>
#if defined(_WIN32)
// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
// them like we do below.
using google::protobuf::internal::win32::open;
#endif
// NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
// error cases, so it seems to be ok to use as a back door for errors.
......
......@@ -36,18 +36,12 @@
#include <set>
#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
#define STDOUT_FILENO 1
#endif
#else
#include <unistd.h>
#endif
#include <google/protobuf/stubs/io_win32.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/compiler/plugin.pb.h>
......@@ -55,6 +49,11 @@
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#if defined(_WIN32)
// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
// them like we do below.
using google::protobuf::internal::win32::setmode;
#endif
namespace google {
namespace protobuf {
......@@ -150,8 +149,8 @@ int PluginMain(int argc, char* argv[], const CodeGenerator* generator) {
}
#ifdef _WIN32
_setmode(STDIN_FILENO, _O_BINARY);
_setmode(STDOUT_FILENO, _O_BINARY);
setmode(STDIN_FILENO, _O_BINARY);
setmode(STDOUT_FILENO, _O_BINARY);
#endif
CodeGeneratorRequest request;
......
......@@ -32,9 +32,7 @@
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifdef _MSC_VER
#include <io.h>
#else
#ifndef _MSC_VER
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
......@@ -43,9 +41,9 @@
#include <errno.h>
#include <iostream>
#include <algorithm>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/io_win32.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/stl_util.h>
......@@ -58,6 +56,13 @@ namespace io {
// Win32 lseek is broken: If invoked on a non-seekable file descriptor, its
// return value is undefined. We re-define it to always produce an error.
#define lseek(fd, offset, origin) ((off_t)-1)
// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
// them like we do below.
using google::protobuf::internal::win32::access;
using google::protobuf::internal::win32::close;
using google::protobuf::internal::win32::open;
using google::protobuf::internal::win32::read;
using google::protobuf::internal::win32::write;
#endif
namespace {
......
......@@ -47,9 +47,7 @@
// implementations.
#ifdef _MSC_VER
#include <io.h>
#else
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <stdlib.h>
......@@ -72,6 +70,7 @@
#endif
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/io_win32.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/testing/googletest.h>
#include <google/protobuf/testing/file.h>
......@@ -84,6 +83,12 @@ namespace {
#ifdef _WIN32
#define pipe(fds) _pipe(fds, 4096, O_BINARY)
// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
// them like we do below.
using google::protobuf::internal::win32::access;
using google::protobuf::internal::win32::mkdir;
using google::protobuf::internal::win32::open;
using google::protobuf::internal::win32::close;
#endif
#ifndef O_BINARY
......
......@@ -37,9 +37,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef _MSC_VER
#include <io.h>
#else
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <sstream>
......@@ -56,6 +54,7 @@
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/io_win32.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
......@@ -63,6 +62,13 @@
namespace google {
namespace protobuf {
#if defined(_WIN32)
// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
// them like we do below.
using google::protobuf::internal::win32::close;
using google::protobuf::internal::win32::open;
#endif
#ifndef O_BINARY
#ifdef _O_BINARY
#define O_BINARY _O_BINARY
......
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: laszlocsomor@google.com (Laszlo Csomor)
//
// Implementation for long-path-aware open/mkdir/etc. on Windows.
//
// These functions convert the input path to an absolute Windows path
// with "\\?\" prefix if necessary, then pass that to _wopen/_wmkdir/etc.
// (declared in <io.h>) respectively. This allows working with files/directories
// whose paths are longer than MAX_PATH (260 chars).
//
// This file is only used on Windows, it's empty on other platforms.
#if defined(_WIN32)
// Comment this out to fall back to using the ANSI versions (open, mkdir, ...)
// instead of the Unicode ones (_wopen, _wmkdir, ...). Doing so can be useful to
// debug failing tests if that's caused by the long path support.
#define SUPPORT_LONGPATHS
#include <ctype.h>
#include <direct.h>
#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <wctype.h>
#include <windows.h>
#include <google/protobuf/stubs/io_win32.h>
#include <cassert>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
namespace google {
namespace protobuf {
namespace internal {
namespace win32 {
namespace {
using std::string;
using std::unique_ptr;
using std::wstring;
template <typename char_type>
struct CharTraits {
static bool is_alpha(char_type ch);
};
template <>
struct CharTraits<char> {
static bool is_alpha(char ch) { return isalpha(ch); }
};
template <>
struct CharTraits<wchar_t> {
static bool is_alpha(wchar_t ch) { return iswalpha(ch); }
};
// Returns true if the path starts with a drive letter, e.g. "c:".
// Note that this won't check for the "\" after the drive letter, so this also
// returns true for "c:foo" (which is "c:\${PWD}\foo").
// This check requires that a path not have a longpath prefix ("\\?\").
template <typename char_type>
bool has_drive_letter(const char_type* ch) {
return CharTraits<char_type>::is_alpha(ch[0]) && ch[1] == ':';
}
// Returns true if the path starts with a longpath prefix ("\\?\").
template <typename char_type>
bool has_longpath_prefix(const char_type* path) {
return path[0] == '\\' && path[1] == '\\' && path[2] == '?' &&
path[3] == '\\';
}
// Returns true if the path starts with a drive specifier (e.g. "c:\").
template <typename char_type>
bool is_path_absolute(const char_type* path) {
return has_drive_letter(path) && is_separator(path[2]);
}
template <typename char_type>
bool is_separator(char_type c) {
return c == '/' || c == '\\';
}
template <typename char_type>
bool is_drive_relative(const char_type* path) {
return has_drive_letter(path) && (path[2] == 0 || !is_separator(path[2]));
}
template <typename char_type>
void replace_directory_separators(char_type* p) {
for (; *p; ++p) {
if (*p == '/') {
*p = '\\';
}
}
}
string join_paths(const string& path1, const string& path2) {
if (path1.empty() || is_path_absolute(path2.c_str()) ||
has_longpath_prefix(path2.c_str())) {
return path2;
}
if (path2.empty()) {
return path1;
}
if (is_separator(path1.back())) {
return is_separator(path2.front()) ? (path1 + path2.substr(1))
: (path1 + path2);
} else {
return is_separator(path2.front()) ? (path1 + path2)
: (path1 + '\\' + path2);
}
}
string normalize(string path) {
if (has_longpath_prefix(path.c_str())) {
path = path.substr(4);
}
static const string dot(".");
static const string dotdot("..");
std::vector<string> segments;
int segment_start = -1;
// Find the path segments in `path` (separated by "/").
for (int i = 0;; ++i) {
if (!is_separator(path[i]) && path[i] != '\0') {
// The current character does not end a segment, so start one unless it's
// already started.
if (segment_start < 0) {
segment_start = i;
}
} else if (segment_start >= 0 && i > segment_start) {
// The current character is "/" or "\0", so this ends a segment.
// Add that to `segments` if there's anything to add; handle "." and "..".
string segment(path, segment_start, i - segment_start);
segment_start = -1;
if (segment == dotdot) {
if (!segments.empty() &&
(!has_drive_letter(segments[0].c_str()) || segments.size() > 1)) {
segments.pop_back();
}
} else if (segment != dot && !segment.empty()) {
segments.push_back(segment);
}
}
if (path[i] == '\0') {
break;
}
}
// Handle the case when `path` is just a drive specifier (or some degenerate
// form of it, e.g. "c:\..").
if (segments.size() == 1 && segments[0].size() == 2 &&
has_drive_letter(segments[0].c_str())) {
return segments[0] + '\\';
}
// Join all segments.
bool first = true;
std::ostringstream result;
for (const auto& s : segments) {
if (!first) {
result << '\\';
}
first = false;
result << s;
}
// Preserve trailing separator if the input contained it.
if (is_separator(path.back())) {
result << '\\';
}
return result.str();
}
std::unique_ptr<WCHAR[]> as_wstring(const string& s) {
int len = ::MultiByteToWideChar(CP_UTF8, 0, s.c_str(), s.size(), NULL, 0);
std::unique_ptr<WCHAR[]> result(new WCHAR[len + 1]);
::MultiByteToWideChar(CP_UTF8, 0, s.c_str(), s.size(), result.get(), len + 1);
result.get()[len] = 0;
return std::move(result);
}
wstring as_wchar_path(const string& path) {
std::unique_ptr<WCHAR[]> wbuf(as_wstring(path));
replace_directory_separators(wbuf.get());
return wstring(wbuf.get());
}
bool as_windows_path(const string& path, wstring* result) {
if (path.empty()) {
result->clear();
return true;
}
if (is_separator(path[0]) || is_drive_relative(path.c_str())) {
return false;
}
string mutable_path = path;
if (!is_path_absolute(mutable_path.c_str()) &&
!has_longpath_prefix(mutable_path.c_str())) {
char cwd[MAX_PATH];
::GetCurrentDirectoryA(MAX_PATH, cwd);
mutable_path = join_paths(cwd, mutable_path);
}
*result = as_wchar_path(normalize(mutable_path));
if (!has_longpath_prefix(result->c_str())) {
// Add the "\\?\" prefix unconditionally. This way we prevent the Win32 API
// from processing the path and "helpfully" removing trailing dots from the
// path, for example.
// See https://github.com/bazelbuild/bazel/issues/2935
*result = wstring(L"\\\\?\\") + *result;
}
return true;
}
} // namespace
int open(const char* path, int flags, int mode) {
#ifdef SUPPORT_LONGPATHS
wstring wpath;
if (!as_windows_path(path, &wpath)) {
errno = ENOENT;
return -1;
}
return ::_wopen(wpath.c_str(), flags, mode);
#else
return ::_open(path, flags, mode);
#endif
}
int mkdir(const char* path, int _mode) {
#ifdef SUPPORT_LONGPATHS
wstring wpath;
if (!as_windows_path(path, &wpath)) {
errno = ENOENT;
return -1;
}
return ::_wmkdir(wpath.c_str());
#else // not SUPPORT_LONGPATHS
return ::_mkdir(path);
#endif // not SUPPORT_LONGPATHS
}
int access(const char* path, int mode) {
#ifdef SUPPORT_LONGPATHS
wstring wpath;
if (!as_windows_path(path, &wpath)) {
errno = ENOENT;
return -1;
}
return ::_waccess(wpath.c_str(), mode);
#else
return ::_access(path, mode);
#endif
}
int chdir(const char* path) {
#ifdef SUPPORT_LONGPATHS
wstring wpath;
if (!as_windows_path(path, &wpath)) {
errno = ENOENT;
return -1;
}
return ::_wchdir(wpath.c_str());
#else
return ::_chdir(path);
#endif
}
int stat(const char* path, struct _stat* buffer) {
#ifdef SUPPORT_LONGPATHS
wstring wpath;
if (!as_windows_path(path, &wpath)) {
errno = ENOENT;
return -1;
}
return ::_wstat(wpath.c_str(), buffer);
#else // not SUPPORT_LONGPATHS
return ::_stat(path, buffer);
#endif // not SUPPORT_LONGPATHS
}
FILE* fopen(const char* path, const char* mode) {
#ifdef SUPPORT_LONGPATHS
wstring wpath;
if (!as_windows_path(path, &wpath)) {
errno = ENOENT;
return NULL;
}
std::unique_ptr<WCHAR[]> wmode(as_wstring(mode));
return ::_wfopen(wpath.c_str(), wmode.get());
#else
return ::fopen(path, mode);
#endif
}
int close(int fd) { return ::close(fd); }
int dup(int fd) { return ::_dup(fd); }
int dup2(int fd1, int fd2) { return ::_dup2(fd1, fd2); }
int read(int fd, void* buffer, size_t size) {
return ::_read(fd, buffer, size);
}
int setmode(int fd, int mode) { return ::_setmode(fd, mode); }
int write(int fd, const void* buffer, size_t size) {
return ::_write(fd, buffer, size);
}
wstring testonly_path_to_winpath(const string& path) {
wstring wpath;
as_windows_path(path, &wpath);
return wpath;
}
} // namespace win32
} // namespace internal
} // namespace protobuf
} // namespace google
#endif // defined(_WIN32)
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: laszlocsomor@google.com (Laszlo Csomor)
//
// This file contains the declarations for Windows implementations of
// commonly used POSIX functions such as open(2) and access(2), as well
// as macro definitions for flags of these functions.
//
// By including this file you'll redefine open/access/etc. to
// ::google::protobuf::internal::win32::{open/access/etc.}.
// Make sure you don't include a header that attempts to redeclare or
// redefine these functions, that'll lead to confusing compilation
// errors. It's best to #include this file as the last one to ensure that.
//
// This file is only used on Windows, it's empty on other platforms.
#ifndef GOOGLE_PROTOBUF_STUBS_IO_WIN32_H__
#define GOOGLE_PROTOBUF_STUBS_IO_WIN32_H__
#if defined(_WIN32)
#include <string>
namespace google {
namespace protobuf {
namespace internal {
namespace win32 {
FILE* fopen(const char* path, const char* mode);
int access(const char* path, int mode);
int chdir(const char* path);
int close(int fd);
int dup(int fd);
int dup2(int fd1, int fd2);
int mkdir(const char* path, int _mode);
int open(const char* path, int flags, int mode = 0);
int read(int fd, void* buffer, size_t size);
int setmode(int fd, int mode);
int stat(const char* path, struct _stat* buffer);
int write(int fd, const void* buffer, size_t size);
std::wstring testonly_path_to_winpath(const std::string& path);
} // namespace win32
} // namespace internal
} // namespace protobuf
} // namespace google
#ifndef W_OK
#define W_OK 02 // not defined by MSVC for whatever reason
#endif
#ifndef F_OK
#define F_OK 00 // not defined by MSVC for whatever reason
#endif
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
#define STDOUT_FILENO 1
#endif
#endif // defined(_WIN32)
#endif // GOOGLE_PROTOBUF_STUBS_IO_WIN32_H__
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: laszlocsomor@google.com (Laszlo Csomor)
//
// Unit tests for long-path-aware open/mkdir/access on Windows.
//
// This file is only used on Windows, it's empty on other platforms.
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <wchar.h>
#include <windows.h>
#include <google/protobuf/stubs/io_win32.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include <string>
namespace google {
namespace protobuf {
namespace internal {
namespace win32 {
namespace {
using std::string;
using std::unique_ptr;
using std::wstring;
class IoWin32Test : public ::testing::Test {
public:
void SetUp() override;
void TearDown() override;
protected:
bool CreateAllUnder(wstring path);
bool DeleteAllUnder(wstring path);
string test_tmpdir;
wstring wtest_tmpdir;
};
#define ASSERT_INITIALIZED \
{ \
EXPECT_FALSE(test_tmpdir.empty()); \
EXPECT_FALSE(wtest_tmpdir.empty()); \
}
void IoWin32Test::SetUp() {
test_tmpdir = string(TestTempDir());
wtest_tmpdir.clear();
if (test_tmpdir.empty()) {
const char* test_tmpdir_env = getenv("TEST_TMPDIR");
if (test_tmpdir_env != nullptr && *test_tmpdir_env) {
test_tmpdir = string(test_tmpdir_env);
}
// Only Bazel defines TEST_TMPDIR, CMake does not, so look for other
// suitable environment variables.
if (test_tmpdir.empty()) {
for (const char* name : {"TEMP", "TMP"}) {
test_tmpdir_env = getenv(name);
if (test_tmpdir_env != nullptr && *test_tmpdir_env) {
test_tmpdir = string(test_tmpdir_env);
break;
}
}
}
// No other temp directory was found. Use the current director
if (test_tmpdir.empty()) {
char buffer[MAX_PATH];
// Use GetCurrentDirectoryA instead of GetCurrentDirectoryW, because the
// current working directory must always be shorter than MAX_PATH, even
// with
// "\\?\" prefix (except on Windows 10 version 1607 and beyond, after
// opting in to long paths by default [1]).
//
// [1] https://msdn.microsoft.com/en-us/library/windows/ \
// desktop/aa365247(v=vs.85).aspx#maxpath
DWORD result = ::GetCurrentDirectoryA(MAX_PATH, buffer);
if (result > 0) {
test_tmpdir = string(buffer);
} else {
// Using assertions in SetUp/TearDown seems to confuse the test
// framework, so just leave the member variables empty in case of
// failure.
GOOGLE_CHECK_OK(false);
return;
}
}
}
while (test_tmpdir.back() == '/' || test_tmpdir.back() == '\\') {
test_tmpdir.pop_back();
}
test_tmpdir += "\\io_win32_unittest.tmp";
// CreateDirectoryA's limit is 248 chars, see MSDN.
// https://msdn.microsoft.com/en-us/library/windows/ \
// desktop/aa363855(v=vs.85).aspx
wtest_tmpdir = testonly_path_to_winpath(test_tmpdir);
if (!DeleteAllUnder(wtest_tmpdir) || !CreateAllUnder(wtest_tmpdir)) {
GOOGLE_CHECK_OK(false);
test_tmpdir.clear();
wtest_tmpdir.clear();
}
}
void IoWin32Test::TearDown() {
if (!wtest_tmpdir.empty()) {
DeleteAllUnder(wtest_tmpdir);
}
}
bool IoWin32Test::CreateAllUnder(wstring path) {
// Prepend UNC prefix if the path doesn't have it already. Don't bother
// checking if the path is shorter than MAX_PATH, let's just do it
// unconditionally.
if (path.find(L"\\\\?\\") != 0) {
path = wstring(L"\\\\?\\") + path;
}
if (::CreateDirectoryW(path.c_str(), NULL) ||
GetLastError() == ERROR_ALREADY_EXISTS ||
GetLastError() == ERROR_ACCESS_DENIED) {
return true;
}
if (GetLastError() == ERROR_PATH_NOT_FOUND) {
size_t pos = path.find_last_of(L'\\');
if (pos != wstring::npos) {
wstring parent(path, 0, pos);
if (CreateAllUnder(parent) && CreateDirectoryW(path.c_str(), NULL)) {
return true;
}
}
}
return false;
}
bool IoWin32Test::DeleteAllUnder(wstring path) {
static const wstring kDot(L".");
static const wstring kDotDot(L"..");
// Prepend UNC prefix if the path doesn't have it already. Don't bother
// checking if the path is shorter than MAX_PATH, let's just do it
// unconditionally.
if (path.find(L"\\\\?\\") != 0) {
path = wstring(L"\\\\?\\") + path;
}
// Append "\" if necessary.
if (path.back() != '\\') {
path.push_back('\\');
}
WIN32_FIND_DATAW metadata;
HANDLE handle = ::FindFirstFileW((path + L"*").c_str(), &metadata);
if (handle == INVALID_HANDLE_VALUE) {
return true; // directory doesn't exist
}
bool result = true;
do {
wstring childname = metadata.cFileName;
if (kDot != childname && kDotDot != childname) {
wstring childpath = path + childname;
if ((metadata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
// If this is not a junction, delete its contents recursively.
// Finally delete this directory/junction too.
if (((metadata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0 &&
!DeleteAllUnder(childpath)) ||
!::RemoveDirectoryW(childpath.c_str())) {
result = false;
break;
}
} else {
if (!::DeleteFileW(childpath.c_str())) {
result = false;
break;
}
}
}
} while (::FindNextFileW(handle, &metadata));
::FindClose(handle);
return result;
}
TEST_F(IoWin32Test, AccessTest) {
ASSERT_INITIALIZED;
string path = test_tmpdir;
while (path.size() < MAX_PATH - 30) {
path += "\\accesstest";
EXPECT_EQ(mkdir(path.c_str(), 0644), 0);
}
string file = path + "\\file.txt";
int fd = open(file.c_str(), O_CREAT | O_WRONLY, 0644);
if (fd > 0) {
EXPECT_EQ(close(fd), 0);
} else {
EXPECT_TRUE(false);
}
EXPECT_EQ(access(test_tmpdir.c_str(), F_OK), 0);
EXPECT_EQ(access(path.c_str(), F_OK), 0);
EXPECT_EQ(access(path.c_str(), W_OK), 0);
EXPECT_EQ(access(file.c_str(), F_OK | W_OK), 0);
EXPECT_NE(access((file + ".blah").c_str(), F_OK), 0);
EXPECT_NE(access((file + ".blah").c_str(), W_OK), 0);
EXPECT_EQ(access(".", F_OK), 0);
EXPECT_EQ(access(".", W_OK), 0);
EXPECT_EQ(access((test_tmpdir + "/accesstest").c_str(), F_OK | W_OK), 0);
ASSERT_EQ(access((test_tmpdir + "/./normalize_me/.././accesstest").c_str(),
F_OK | W_OK),
0);
EXPECT_NE(access("io_win32_unittest.AccessTest.nonexistent", F_OK), 0);
EXPECT_NE(access("io_win32_unittest.AccessTest.nonexistent", W_OK), 0);
ASSERT_EQ(access("c:bad", F_OK), -1);
ASSERT_EQ(errno, ENOENT);
ASSERT_EQ(access("/tmp/bad", F_OK), -1);
ASSERT_EQ(errno, ENOENT);
ASSERT_EQ(access("\\bad", F_OK), -1);
ASSERT_EQ(errno, ENOENT);
}
TEST_F(IoWin32Test, OpenTest) {
ASSERT_INITIALIZED;
string path = test_tmpdir;
while (path.size() < MAX_PATH) {
path += "\\opentest";
EXPECT_EQ(mkdir(path.c_str(), 0644), 0);
}
string file = path + "\\file.txt";
int fd = open(file.c_str(), O_CREAT | O_WRONLY, 0644);
if (fd > 0) {
EXPECT_EQ(write(fd, "hello", 5), 5);
EXPECT_EQ(close(fd), 0);
} else {
EXPECT_TRUE(false);
}
ASSERT_EQ(open("c:bad.txt", O_CREAT | O_WRONLY, 0644), -1);
ASSERT_EQ(errno, ENOENT);
ASSERT_EQ(open("/tmp/bad.txt", O_CREAT | O_WRONLY, 0644), -1);
ASSERT_EQ(errno, ENOENT);
ASSERT_EQ(open("\\bad.txt", O_CREAT | O_WRONLY, 0644), -1);
ASSERT_EQ(errno, ENOENT);
}
TEST_F(IoWin32Test, MkdirTest) {
ASSERT_INITIALIZED;
string path = test_tmpdir;
do {
path += "\\mkdirtest";
ASSERT_EQ(mkdir(path.c_str(), 0644), 0);
} while (path.size() <= MAX_PATH);
ASSERT_EQ(mkdir("c:bad", 0644), -1);
ASSERT_EQ(errno, ENOENT);
ASSERT_EQ(mkdir("/tmp/bad", 0644), -1);
ASSERT_EQ(errno, ENOENT);
ASSERT_EQ(mkdir("\\bad", 0644), -1);
ASSERT_EQ(errno, ENOENT);
}
TEST_F(IoWin32Test, ChdirTest) {
char owd[MAX_PATH];
EXPECT_GT(::GetCurrentDirectoryA(MAX_PATH, owd), 0);
string path("C:\\");
EXPECT_EQ(access(path.c_str(), F_OK), 0);
ASSERT_EQ(chdir(path.c_str()), 0);
EXPECT_TRUE(::SetCurrentDirectoryA(owd));
// Do not try to chdir into the test_tmpdir, it may already contain directory
// names with trailing dots.
// Instead test here with an obviously dot-trailed path. If the win32_chdir
// function would not convert the path to absolute and prefix with "\\?\" then
// the Win32 API would ignore the trailing dot, but because of the prefixing
// there'll be no path processing done, so we'll actually attempt to chdir
// into "C:\some\path\foo."
path = test_tmpdir + "/foo.";
EXPECT_EQ(mkdir(path.c_str(), 644), 0);
EXPECT_EQ(access(path.c_str(), F_OK), 0);
ASSERT_NE(chdir(path.c_str()), 0);
}
TEST_F(IoWin32Test, AsWindowsPathTest) {
DWORD size = GetCurrentDirectoryW(0, NULL);
unique_ptr<wchar_t[]> cwd_str(new wchar_t[size]);
EXPECT_GT(GetCurrentDirectoryW(size, cwd_str.get()), 0);
wstring cwd = wstring(L"\\\\?\\") + cwd_str.get();
ASSERT_EQ(testonly_path_to_winpath("relative_mkdirtest"),
cwd + L"\\relative_mkdirtest");
ASSERT_EQ(testonly_path_to_winpath("preserve//\\trailing///"),
cwd + L"\\preserve\\trailing\\");
ASSERT_EQ(testonly_path_to_winpath("./normalize_me\\/../blah"),
cwd + L"\\blah");
std::ostringstream relpath;
for (wchar_t* p = cwd_str.get(); *p; ++p) {
if (*p == '/' || *p == '\\') {
relpath << "../";
}
}
relpath << ".\\/../\\./beyond-toplevel";
ASSERT_EQ(testonly_path_to_winpath(relpath.str()),
wstring(L"\\\\?\\") + cwd_str.get()[0] + L":\\beyond-toplevel");
// Absolute unix paths lack drive letters, driveless absolute windows paths
// do too. Neither can be converted to a drive-specifying absolute Windows
// path.
ASSERT_EQ(testonly_path_to_winpath("/absolute/unix/path"), L"");
// Though valid on Windows, we also don't support UNC paths (\\UNC\\blah).
ASSERT_EQ(testonly_path_to_winpath("\\driveless\\absolute"), L"");
// Though valid in cmd.exe, drive-relative paths are not supported.
ASSERT_EQ(testonly_path_to_winpath("c:foo"), L"");
ASSERT_EQ(testonly_path_to_winpath("c:/foo"), L"\\\\?\\c:\\foo");
}
} // namespace
} // namespace win32
} // namespace internal
} // namespace protobuf
} // namespace google
#endif // defined(_WIN32)
......@@ -38,24 +38,28 @@
#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN // yeah, right
#include <windows.h> // Find*File(). :(
#include <io.h>
#include <direct.h>
// #include <direct.h>
#else
#include <dirent.h>
#include <unistd.h>
#endif
#include <errno.h>
#include <google/protobuf/stubs/io_win32.h>
namespace google {
namespace protobuf {
#ifdef _WIN32
#define mkdir(name, mode) mkdir(name)
// Windows doesn't have symbolic links.
#define lstat stat
#ifndef F_OK
#define F_OK 00 // not defined by MSVC for whatever reason
#endif
// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
// them like we do below.
using google::protobuf::internal::win32::access;
using google::protobuf::internal::win32::chdir;
using google::protobuf::internal::win32::fopen;
using google::protobuf::internal::win32::mkdir;
using google::protobuf::internal::win32::stat;
#endif
bool File::Exists(const string& name) {
......@@ -113,6 +117,9 @@ void File::WriteStringToFileOrDie(const string& contents, const string& name) {
}
bool File::CreateDir(const string& name, int mode) {
if (!name.empty()) {
GOOGLE_CHECK_OK(name.back() != '.');
}
return mkdir(name.c_str(), mode) == 0;
}
......
......@@ -33,14 +33,14 @@
#include <google/protobuf/testing/googletest.h>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/stubs/io_win32.h>
#include <google/protobuf/stubs/strutil.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#ifdef _MSC_VER
#include <io.h>
#include <direct.h>
// #include <direct.h>
#else
#include <unistd.h>
#endif
......@@ -53,7 +53,13 @@ namespace google {
namespace protobuf {
#ifdef _WIN32
#define mkdir(name, mode) mkdir(name)
// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
// them like we do below.
using google::protobuf::internal::win32::close;
using google::protobuf::internal::win32::dup2;
using google::protobuf::internal::win32::dup;
using google::protobuf::internal::win32::mkdir;
using google::protobuf::internal::win32::open;
#endif
#ifndef O_BINARY
......@@ -111,14 +117,32 @@ string GetTemporaryDirectoryName() {
char b[L_tmpnam + 1]; // HPUX multithread return 0 if s is 0
string result = tmpnam(b);
#ifdef _WIN32
// Avoid a trailing dot by changing it to an underscore. On Win32 the names of
// files and directories can, but should not, end with dot.
//
// In MS-DOS and FAT16 filesystem the filenames were 8dot3 style so it didn't
// make sense to have a name ending in dot without an extension, so the shell
// silently ignored trailing dots. To this day the Win32 API still maintains
// this behavior and silently ignores trailing dots in path arguments of
// functions such as CreateFile{A,W}. Even POSIX API function implementations
// seem to wrap the Win32 API functions (e.g. CreateDirectoryA) and behave
// this way.
// It's possible to avoid this behavior and create files / directories with
// trailing dots (using CreateFileW / CreateDirectoryW and prefixing the path
// with "\\?\") but these will be degenerate in the sense that you cannot
// chdir into such directories (or navigate into them with Windows Explorer)
// nor can you open such files with some programs (e.g. Notepad).
if (result.back() == '.') {
result[result.size() - 1] = '_';
}
// On Win32, tmpnam() returns a file prefixed with '\', but which is supposed
// to be used in the current working directory. WTF?
if (HasPrefixString(result, "\\")) {
result.erase(0, 1);
}
// The Win32 API accepts forward slashes as a path delimiter even though
// backslashes are standard. Let's avoid confusion and use only forward
// slashes.
// The Win32 API accepts forward slashes as a path delimiter as long as the
// path doesn't use the "\\?\" prefix.
// Let's avoid confusion and use only forward slashes.
result = StringReplace(result, "\\", "/", true);
#endif // _WIN32
return result;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment