diff options
Diffstat (limited to 'indra/llcommon/llfile.cpp')
-rw-r--r-- | indra/llcommon/llfile.cpp | 187 |
1 files changed, 163 insertions, 24 deletions
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index c32a776c3f..c51d042a3d 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llfile.cpp * @author Michael Schlachter * @date 2006-03-23 @@ -8,60 +8,194 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #if LL_WINDOWS #include <windows.h> +#include <stdlib.h> // Windows errno +#else +#include <errno.h> #endif #include "linden_common.h" #include "llfile.h" #include "llstring.h" #include "llerror.h" +#include "stringize.h" using namespace std; +static std::string empty; + +// Many of the methods below use OS-level functions that mess with errno. Wrap +// variants of strerror() to report errors. + +#if LL_WINDOWS +// On Windows, use strerror_s(). +std::string strerr(int errn) +{ + char buffer[256]; + strerror_s(buffer, errn); // infers sizeof(buffer) -- love it! + return buffer; +} + +#else +// On Posix we want to call strerror_r(), but alarmingly, there are two +// different variants. The one that returns int always populates the passed +// buffer (except in case of error), whereas the other one always returns a +// valid char* but might or might not populate the passed buffer. How do we +// know which one we're getting? Define adapters for each and let the compiler +// select the applicable adapter. + +// strerror_r() returns char* +std::string message_from(int /*orig_errno*/, const char* /*buffer*/, size_t /*bufflen*/, + const char* strerror_ret) +{ + return strerror_ret; +} + +// strerror_r() returns int +std::string message_from(int orig_errno, const char* buffer, size_t bufflen, + int strerror_ret) +{ + if (strerror_ret == 0) + { + return buffer; + } + // Here strerror_r() has set errno. Since strerror_r() has already failed, + // seems like a poor bet to call it again to diagnose its own error... + int stre_errno = errno; + if (stre_errno == ERANGE) + { + return STRINGIZE("strerror_r() can't explain errno " << orig_errno + << " (" << bufflen << "-byte buffer too small)"); + } + if (stre_errno == EINVAL) + { + return STRINGIZE("unknown errno " << orig_errno); + } + // Here we don't even understand the errno from strerror_r()! + return STRINGIZE("strerror_r() can't explain errno " << orig_errno + << " (error " << stre_errno << ')'); +} + +std::string strerr(int errn) +{ + char buffer[256]; + // Select message_from() function matching the strerror_r() we have on hand. + return message_from(errn, buffer, sizeof(buffer), + strerror_r(errn, buffer, sizeof(buffer))); +} +#endif // ! LL_WINDOWS + +// On either system, shorthand call just infers global 'errno'. +std::string strerr() +{ + return strerr(errno); +} + +int warnif(const std::string& desc, const std::string& filename, int rc, int accept=0) +{ + if (rc < 0) + { + // Capture errno before we start emitting output + int errn = errno; + // For certain operations, a particular errno value might be + // acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit + // EEXIST. Don't warn if caller explicitly says this errno is okay. + if (errn != accept) + { + LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename + << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL; + } +#if 0 && LL_WINDOWS // turn on to debug file-locking problems + // If the problem is "Permission denied," maybe it's because another + // process has the file open. Try to find out. + if (errn == EACCES) // *not* EPERM + { + // Only do any of this stuff (before LL_ENDL) if it will be logged. + LL_DEBUGS("LLFile") << empty; + const char* TEMP = getenv("TEMP"); + if (! TEMP) + { + LL_CONT << "No $TEMP, not running 'handle'"; + } + else + { + std::string tf(TEMP); + tf += "\\handle.tmp"; + // http://technet.microsoft.com/en-us/sysinternals/bb896655 + std::string cmd(STRINGIZE("handle \"" << filename + // "openfiles /query /v | fgrep -i \"" << filename + << "\" > \"" << tf << '"')); + LL_CONT << cmd; + if (system(cmd.c_str()) != 0) + { + LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655"; + } + else + { + std::ifstream inf(tf); + std::string line; + while (std::getline(inf, line)) + { + LL_CONT << '\n' << line; + } + } + LLFile::remove(tf); + } + LL_CONT << LL_ENDL; + } +#endif // LL_WINDOWS hack to identify processes holding file open + } + return rc; +} + // static int LLFile::mkdir(const std::string& dirname, int perms) { -#if LL_WINDOWS +#if LL_WINDOWS // permissions are ignored on Windows std::string utf8dirname = dirname; llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname); - return _wmkdir(utf16dirname.c_str()); + int rc = _wmkdir(utf16dirname.c_str()); #else - return ::mkdir(dirname.c_str(), (mode_t)perms); + int rc = ::mkdir(dirname.c_str(), (mode_t)perms); #endif + // We often use mkdir() to ensure the existence of a directory that might + // already exist. Don't spam the log if it does. + return warnif("mkdir", dirname, rc, EEXIST); } // static int LLFile::rmdir(const std::string& dirname) { -#if LL_WINDOWS +#if LL_WINDOWS // permissions are ignored on Windows std::string utf8dirname = dirname; llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname); - return _wrmdir(utf16dirname.c_str()); + int rc = _wrmdir(utf16dirname.c_str()); #else - return ::rmdir(dirname.c_str()); + int rc = ::rmdir(dirname.c_str()); #endif + return warnif("rmdir", dirname, rc); } // static @@ -108,10 +242,11 @@ int LLFile::remove(const std::string& filename) #if LL_WINDOWS std::string utf8filename = filename; llutf16string utf16filename = utf8str_to_utf16str(utf8filename); - return _wremove(utf16filename.c_str()); + int rc = _wremove(utf16filename.c_str()); #else - return ::remove(filename.c_str()); + int rc = ::remove(filename.c_str()); #endif + return warnif("remove", filename, rc); } int LLFile::rename(const std::string& filename, const std::string& newname) @@ -121,10 +256,11 @@ int LLFile::rename(const std::string& filename, const std::string& newname) std::string utf8newname = newname; llutf16string utf16filename = utf8str_to_utf16str(utf8filename); llutf16string utf16newname = utf8str_to_utf16str(utf8newname); - return _wrename(utf16filename.c_str(),utf16newname.c_str()); + int rc = _wrename(utf16filename.c_str(),utf16newname.c_str()); #else - return ::rename(filename.c_str(),newname.c_str()); + int rc = ::rename(filename.c_str(),newname.c_str()); #endif + return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc); } int LLFile::stat(const std::string& filename, llstat* filestatus) @@ -132,23 +268,26 @@ int LLFile::stat(const std::string& filename, llstat* filestatus) #if LL_WINDOWS std::string utf8filename = filename; llutf16string utf16filename = utf8str_to_utf16str(utf8filename); - return _wstat(utf16filename.c_str(),filestatus); + int rc = _wstat(utf16filename.c_str(),filestatus); #else - return ::stat(filename.c_str(),filestatus); + int rc = ::stat(filename.c_str(),filestatus); #endif + // We use stat() to determine existence (see isfile(), isdir()). + // Don't spam the log if the subject pathname doesn't exist. + return warnif("stat", filename, rc, ENOENT); } bool LLFile::isdir(const std::string& filename) { llstat st; - + return stat(filename, &st) == 0 && S_ISDIR(st.st_mode); } bool LLFile::isfile(const std::string& filename) { llstat st; - + return stat(filename, &st) == 0 && S_ISREG(st.st_mode); } @@ -260,7 +399,7 @@ void llifstream::open(const std::string& _Filename, /* Flawfinder: ignore */ ios_base::openmode _Mode, int _Prot) { // open a C stream with specified mode - + LLFILE* filep = LLFile::_Fiopen(_Filename,_Mode | ios_base::in, _Prot); if(filep == NULL) { @@ -280,7 +419,7 @@ bool llifstream::is_open() const return false; } llifstream::~llifstream() -{ +{ if (_ShouldClose) { close(); @@ -309,7 +448,7 @@ bool llofstream::is_open() const void llofstream::open(const std::string& _Filename, /* Flawfinder: ignore */ ios_base::openmode _Mode, - int _Prot) + int _Prot) { // open a C stream with specified mode LLFILE* filep = LLFile::_Fiopen(_Filename,_Mode | ios_base::out, _Prot); @@ -340,14 +479,14 @@ void llofstream::close() llofstream::llofstream(const std::string& _Filename, std::ios_base::openmode _Mode, - int _Prot) + int _Prot) : std::basic_ostream<char,std::char_traits < char > >(NULL,true),_Filebuffer(NULL),_ShouldClose(false) { // construct with named file and specified mode open(_Filename, _Mode , _Prot); /* Flawfinder: ignore */ } llofstream::~llofstream() -{ +{ // destroy the object if (_ShouldClose) { |