1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
|
/**
* @file llfile.cpp
* @author Michael Schlachter
* @date 2006-03-23
* @brief Implementation of cross-platform POSIX file buffer and c++
* stream classes.
*
* $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$
*/
#include "linden_common.h"
#include "llfile.h"
#include "llstring.h"
#include "llerror.h"
#include "stringize.h"
#if LL_WINDOWS
#include "llwin32headers.h"
#include <vector>
#else
#include <errno.h>
#endif
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
// For the situations where we directly call into Windows API functions we need to translate
// the Windows error codes into errno values
namespace
{
struct errentry
{
unsigned long oserr; // Windows OS error value
int errcode; // System V error code
};
}
// translation table between Windows OS error value and System V errno code
static errentry const errtable[]
{
{ ERROR_INVALID_FUNCTION, EINVAL }, // 1
{ ERROR_FILE_NOT_FOUND, ENOENT }, // 2
{ ERROR_PATH_NOT_FOUND, ENOENT }, // 3
{ ERROR_TOO_MANY_OPEN_FILES, EMFILE }, // 4
{ ERROR_ACCESS_DENIED, EACCES }, // 5
{ ERROR_INVALID_HANDLE, EBADF }, // 6
{ ERROR_ARENA_TRASHED, ENOMEM }, // 7
{ ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, // 8
{ ERROR_INVALID_BLOCK, ENOMEM }, // 9
{ ERROR_BAD_ENVIRONMENT, E2BIG }, // 10
{ ERROR_BAD_FORMAT, ENOEXEC }, // 11
{ ERROR_INVALID_ACCESS, EINVAL }, // 12
{ ERROR_INVALID_DATA, EINVAL }, // 13
{ ERROR_INVALID_DRIVE, ENOENT }, // 15
{ ERROR_CURRENT_DIRECTORY, EACCES }, // 16
{ ERROR_NOT_SAME_DEVICE, EXDEV }, // 17
{ ERROR_NO_MORE_FILES, ENOENT }, // 18
{ ERROR_LOCK_VIOLATION, EACCES }, // 33
{ ERROR_BAD_NETPATH, ENOENT }, // 53
{ ERROR_NETWORK_ACCESS_DENIED, EACCES }, // 65
{ ERROR_BAD_NET_NAME, ENOENT }, // 67
{ ERROR_FILE_EXISTS, EEXIST }, // 80
{ ERROR_CANNOT_MAKE, EACCES }, // 82
{ ERROR_FAIL_I24, EACCES }, // 83
{ ERROR_INVALID_PARAMETER, EINVAL }, // 87
{ ERROR_NO_PROC_SLOTS, EAGAIN }, // 89
{ ERROR_DRIVE_LOCKED, EACCES }, // 108
{ ERROR_BROKEN_PIPE, EPIPE }, // 109
{ ERROR_DISK_FULL, ENOSPC }, // 112
{ ERROR_INVALID_TARGET_HANDLE, EBADF }, // 114
{ ERROR_WAIT_NO_CHILDREN, ECHILD }, // 128
{ ERROR_CHILD_NOT_COMPLETE, ECHILD }, // 129
{ ERROR_DIRECT_ACCESS_HANDLE, EBADF }, // 130
{ ERROR_NEGATIVE_SEEK, EINVAL }, // 131
{ ERROR_SEEK_ON_DEVICE, EACCES }, // 132
{ ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, // 145
{ ERROR_NOT_LOCKED, EACCES }, // 158
{ ERROR_BAD_PATHNAME, ENOENT }, // 161
{ ERROR_MAX_THRDS_REACHED, EAGAIN }, // 164
{ ERROR_LOCK_FAILED, EACCES }, // 167
{ ERROR_ALREADY_EXISTS, EEXIST }, // 183
{ ERROR_FILENAME_EXCED_RANGE, ENOENT }, // 206
{ ERROR_NESTING_NOT_ALLOWED, EAGAIN }, // 215
{ ERROR_NO_UNICODE_TRANSLATION, EILSEQ }, // 1113
{ ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816
};
static int set_errno_from_oserror(unsigned long oserr)
{
if (!oserr)
return 0;
// Check the table for the Windows OS error code
for (const struct errentry &entry : errtable)
{
if (oserr == entry.oserr)
{
_set_errno(entry.errcode);
return -1;
}
}
_set_errno(EINVAL);
return -1;
}
// On Windows, use strerror_s().
std::string strerr(int errn)
{
char buffer[256];
strerror_s(buffer, errn); // infers sizeof(buffer) -- love it!
return buffer;
}
inline bool is_slash(wchar_t const c)
{
return c == L'\\' || c == L'/';
}
static std::wstring utf8path_to_wstring(const std::string& utf8path)
{
if (utf8path.size() >= MAX_PATH)
{
// By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names
std::wstring utf16path = L"\\\\?\\" + ll_convert<std::wstring>(utf8path);
// We need to make sure that the path does not contain forward slashes as above
// prefix does bypass the path normalization that replaces slashes with backslashes
// before passing the path to kernel mode APIs
std::replace(utf16path.begin(), utf16path.end(), L'/', L'\\');
return utf16path;
}
return ll_convert<std::wstring>(utf8path);
}
static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollowSymLink = false)
{
unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS;
if (dontFollowSymLink)
{
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
}
HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr, OPEN_EXISTING, flags, nullptr);
if (file_handle != INVALID_HANDLE_VALUE)
{
FILE_ATTRIBUTE_TAG_INFO attribute_info;
if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info)))
{
// A volume path alone (only drive letter) is not recognized as directory while it technically is
bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
(iswalpha(utf16path[0]) && utf16path[1] == ':' &&
(!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3])));
unsigned short st_mode = is_directory ? S_IFDIR :
(attribute_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? S_IFLNK : S_IFREG);
st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE;
// we do not try to guess executable flag
// propagate user bits to group/other fields:
st_mode |= (st_mode & 0700) >> 3;
st_mode |= (st_mode & 0700) >> 6;
CloseHandle(file_handle);
return st_mode;
}
}
// Retrieve last error and set errno before calling CloseHandle()
set_errno_from_oserror(GetLastError());
if (file_handle != INVALID_HANDLE_VALUE)
{
CloseHandle(file_handle);
}
return 0;
}
#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
static 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;
// would be nice to use LLDir for this, but dependency goes the
// wrong way
const char* TEMP = LLFile::tmpdir();
if (! (TEMP && *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)
{
// We often use mkdir() to ensure the existence of a directory that might
// already exist. There is no known case in which we want to call out as
// an error the requested directory already existing.
#if LL_WINDOWS
// permissions are ignored on Windows
int rc = 0;
std::wstring utf16dirname = utf8path_to_wstring(dirname);
if (!CreateDirectoryW(utf16dirname.c_str(), nullptr))
{
// Only treat other errors than an already existing file as a real error
unsigned long oserr = GetLastError();
if (oserr != ERROR_ALREADY_EXISTS)
{
rc = set_errno_from_oserror(oserr);
}
}
#else
int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
if (rc < 0 && errno == EEXIST)
{
// this is not the error you want, move along
return 0;
}
#endif
// anything else might be a problem
return warnif("mkdir", dirname, rc);
}
// static
int LLFile::rmdir(const std::string& dirname, int suppress_error)
{
#if LL_WINDOWS
std::wstring utf16dirname = utf8path_to_wstring(dirname);
int rc = _wrmdir(utf16dirname.c_str());
#else
int rc = ::rmdir(dirname.c_str());
#endif
return warnif("rmdir", dirname, rc, suppress_error);
}
// static
LLFILE* LLFile::fopen(const std::string& filename, const char* mode)
{
#if LL_WINDOWS
std::wstring utf16filename = utf8path_to_wstring(filename);
std::wstring utf16mode = ll_convert<std::wstring>(std::string(mode));
return _wfopen(utf16filename.c_str(), utf16mode.c_str());
#else
return ::fopen(filename.c_str(),mode);
#endif
}
// static
int LLFile::close(LLFILE * file)
{
int ret_value = 0;
if (file)
{
ret_value = fclose(file);
}
return ret_value;
}
// static
std::string LLFile::getContents(const std::string& filename)
{
LLFILE* fp = LLFile::fopen(filename, "rb");
if (fp)
{
fseek(fp, 0, SEEK_END);
U32 length = ftell(fp);
fseek(fp, 0, SEEK_SET);
std::vector<char> buffer(length);
size_t nread = fread(buffer.data(), 1, length, fp);
fclose(fp);
return std::string(buffer.data(), nread);
}
return LLStringUtil::null;
}
// static
int LLFile::remove(const std::string& filename, int suppress_error)
{
#if LL_WINDOWS
// Posix remove() works on both files and directories although on Windows
// remove() and its wide char variant _wremove() only removes files just
// as its siblings unlink() and _wunlink().
// If we really only want to support files we should instead use
// unlink() in the non-Windows part below too
int rc = -1;
std::wstring utf16filename = utf8path_to_wstring(filename);
unsigned short st_mode = get_fileattr(utf16filename);
if (S_ISDIR(st_mode))
{
rc = _wrmdir(utf16filename.c_str());
}
else if (S_ISREG(st_mode))
{
rc = _wunlink(utf16filename.c_str());
}
else if (st_mode)
{
// it is something else than a file or directory
// this should not really happen as long as we do not allow for symlink
// detection in the optional parameter to get_fileattr()
rc = set_errno_from_oserror(ERROR_INVALID_PARAMETER);
}
else
{
// get_fileattr() failed and already set errno, preserve it for correct error reporting
}
#else
int rc = ::remove(filename.c_str());
#endif
return warnif("remove", filename, rc, suppress_error);
}
// static
int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error)
{
#if LL_WINDOWS
// Posix rename() will gladly overwrite a file at newname if it exists, the Windows
// rename(), respectively _wrename(), will bark on that. Instead call directly the Windows
// API MoveFileEx() and use its flags to specify that overwrite is allowed.
std::wstring utf16filename = utf8path_to_wstring(filename);
std::wstring utf16newname = utf8path_to_wstring(newname);
int rc = 0;
if (!MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
{
rc = set_errno_from_oserror(GetLastError());
}
#else
int rc = ::rename(filename.c_str(),newname.c_str());
#endif
return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error);
}
// Make this a define rather than using magic numbers multiple times in the code
#define LLFILE_COPY_BUFFER_SIZE 16384
// static
bool LLFile::copy(const std::string& from, const std::string& to)
{
bool copied = false;
LLFILE* in = LLFile::fopen(from, "rb");
if (in)
{
LLFILE* out = LLFile::fopen(to, "wb");
if (out)
{
char buf[LLFILE_COPY_BUFFER_SIZE];
size_t readbytes;
bool write_ok = true;
while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in)))
{
if (fwrite(buf, 1, readbytes, out) != readbytes)
{
LL_WARNS("LLFile") << "Short write" << LL_ENDL;
write_ok = false;
}
}
if ( write_ok )
{
copied = true;
}
fclose(out);
}
fclose(in);
}
return copied;
}
// static
int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_error)
{
#if LL_WINDOWS
std::wstring utf16filename = utf8path_to_wstring(filename);
int rc = _wstat64(utf16filename.c_str(), filestatus);
#else
int rc = ::stat(filename.c_str(), filestatus);
#endif
return warnif("stat", filename, rc, suppress_error);
}
// static
unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error)
{
#if LL_WINDOWS
// _wstat64() is a bit heavyweight on Windows, use a more lightweight API
// to just get the attributes
int rc = -1;
std::wstring utf16filename = utf8path_to_wstring(filename);
unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink);
if (st_mode)
{
return st_mode;
}
#else
llstat filestatus;
int rc = dontFollowSymLink ? ::lstat(filename.c_str(), &filestatus) : ::stat(filename.c_str(), &filestatus);
if (rc == 0)
{
return filestatus.st_mode;
}
#endif
warnif("getattr", filename, rc, suppress_error);
return 0;
}
// static
bool LLFile::isdir(const std::string& filename)
{
return S_ISDIR(getattr(filename));
}
// static
bool LLFile::isfile(const std::string& filename)
{
return S_ISREG(getattr(filename));
}
// static
bool LLFile::islink(const std::string& filename)
{
return S_ISLNK(getattr(filename, true));
}
// static
const char *LLFile::tmpdir()
{
static std::string utf8path;
if (utf8path.empty())
{
char sep;
#if LL_WINDOWS
sep = '\\';
std::vector<wchar_t> utf16path(MAX_PATH + 1);
GetTempPathW(static_cast<DWORD>(utf16path.size()), &utf16path[0]);
utf8path = ll_convert_wide_to_string(&utf16path[0]);
#else
sep = '/';
utf8path = LLStringUtil::getenv("TMPDIR", "/tmp/");
#endif
if (utf8path[utf8path.size() - 1] != sep)
{
utf8path += sep;
}
}
return utf8path.c_str();
}
#if LL_WINDOWS
/************** input file stream ********************************/
llifstream::llifstream() {}
// explicit
llifstream::llifstream(const std::string& _Filename, ios_base::openmode _Mode):
std::ifstream(ll_convert<std::wstring>( _Filename ).c_str(),
_Mode | ios_base::in)
{
}
void llifstream::open(const std::string& _Filename, ios_base::openmode _Mode)
{
std::ifstream::open(ll_convert<std::wstring>(_Filename).c_str(),
_Mode | ios_base::in);
}
/************** output file stream ********************************/
llofstream::llofstream() {}
// explicit
llofstream::llofstream(const std::string& _Filename, ios_base::openmode _Mode):
std::ofstream(ll_convert<std::wstring>( _Filename ).c_str(),
_Mode | ios_base::out)
{
}
void llofstream::open(const std::string& _Filename, ios_base::openmode _Mode)
{
std::ofstream::open(ll_convert<std::wstring>( _Filename ).c_str(),
_Mode | ios_base::out);
}
/************** helper functions ********************************/
std::streamsize llifstream_size(llifstream& ifstr)
{
if(!ifstr.is_open()) return 0;
std::streampos pos_old = ifstr.tellg();
ifstr.seekg(0, ios_base::beg);
std::streampos pos_beg = ifstr.tellg();
ifstr.seekg(0, ios_base::end);
std::streampos pos_end = ifstr.tellg();
ifstr.seekg(pos_old, ios_base::beg);
return pos_end - pos_beg;
}
std::streamsize llofstream_size(llofstream& ofstr)
{
if(!ofstr.is_open()) return 0;
std::streampos pos_old = ofstr.tellp();
ofstr.seekp(0, ios_base::beg);
std::streampos pos_beg = ofstr.tellp();
ofstr.seekp(0, ios_base::end);
std::streampos pos_end = ofstr.tellp();
ofstr.seekp(pos_old, ios_base::beg);
return pos_end - pos_beg;
}
#endif // LL_WINDOWS
|