1//
2// File_UNIX.cpp
3//
4// Library: Foundation
5// Package: Filesystem
6// Module: File
7//
8// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/File_UNIX.h"
16#include "Poco/Buffer.h"
17#include "Poco/Exception.h"
18#include <algorithm>
19#include <sys/stat.h>
20#include <sys/types.h>
21#include <sys/time.h>
22#if defined(POCO_OS_FAMILY_BSD)
23#include <sys/param.h>
24#include <sys/mount.h>
25#elif (POCO_OS == POCO_OS_SOLARIS)
26#include <sys/statvfs.h>
27#else
28#include <sys/statfs.h>
29#endif
30#include <fcntl.h>
31#include <errno.h>
32#include <unistd.h>
33#include <stdio.h>
34#include <cstring>
35
36#if (POCO_OS == POCO_OS_SOLARIS)
37#define STATFSFN statvfs
38#define STATFSSTRUCT statvfs
39#else
40#define STATFSFN statfs
41#define STATFSSTRUCT statfs
42#endif
43
44namespace{
45// Convert timespec structures (seconds and remaining nano secs) to TimeVal (microseconds)
46Poco::Timestamp::TimeVal timespec2Microsecs(const struct timespec &ts) {
47 return ts.tv_sec * 1000000L + ts.tv_nsec / 1000L;
48}
49} // namespace
50
51namespace Poco {
52
53
54FileImpl::FileImpl()
55{
56}
57
58
59FileImpl::FileImpl(const std::string& path): _path(path)
60{
61 std::string::size_type n = _path.size();
62 if (n > 1 && _path[n - 1] == '/')
63 _path.resize(n - 1);
64}
65
66
67FileImpl::~FileImpl()
68{
69}
70
71
72void FileImpl::swapImpl(FileImpl& file)
73{
74 std::swap(_path, file._path);
75}
76
77
78void FileImpl::setPathImpl(const std::string& path)
79{
80 _path = path;
81 std::string::size_type n = _path.size();
82 if (n > 1 && _path[n - 1] == '/')
83 _path.resize(n - 1);
84}
85
86
87bool FileImpl::existsImpl() const
88{
89 poco_assert (!_path.empty());
90
91 struct stat st;
92 return stat(_path.c_str(), &st) == 0;
93}
94
95
96bool FileImpl::canReadImpl() const
97{
98 poco_assert (!_path.empty());
99
100 struct stat st;
101 if (stat(_path.c_str(), &st) == 0)
102 {
103 if (st.st_uid == geteuid())
104 return (st.st_mode & S_IRUSR) != 0;
105 else if (st.st_gid == getegid())
106 return (st.st_mode & S_IRGRP) != 0;
107 else
108 return (st.st_mode & S_IROTH) != 0 || geteuid() == 0;
109 }
110 else handleLastErrorImpl(_path);
111 return false;
112}
113
114
115bool FileImpl::canWriteImpl() const
116{
117 poco_assert (!_path.empty());
118
119 struct stat st;
120 if (stat(_path.c_str(), &st) == 0)
121 {
122 if (st.st_uid == geteuid())
123 return (st.st_mode & S_IWUSR) != 0;
124 else if (st.st_gid == getegid())
125 return (st.st_mode & S_IWGRP) != 0;
126 else
127 return (st.st_mode & S_IWOTH) != 0 || geteuid() == 0;
128 }
129 else handleLastErrorImpl(_path);
130 return false;
131}
132
133
134bool FileImpl::canExecuteImpl() const
135{
136 poco_assert (!_path.empty());
137
138 struct stat st;
139 if (stat(_path.c_str(), &st) == 0)
140 {
141 if (st.st_uid == geteuid() || geteuid() == 0)
142 return (st.st_mode & S_IXUSR) != 0;
143 else if (st.st_gid == getegid())
144 return (st.st_mode & S_IXGRP) != 0;
145 else
146 return (st.st_mode & S_IXOTH) != 0;
147 }
148 else handleLastErrorImpl(_path);
149 return false;
150}
151
152
153bool FileImpl::isFileImpl() const
154{
155 poco_assert (!_path.empty());
156
157 struct stat st;
158 if (stat(_path.c_str(), &st) == 0)
159 return S_ISREG(st.st_mode);
160 else
161 handleLastErrorImpl(_path);
162 return false;
163}
164
165
166bool FileImpl::isDirectoryImpl() const
167{
168 poco_assert (!_path.empty());
169
170 struct stat st;
171 if (stat(_path.c_str(), &st) == 0)
172 return S_ISDIR(st.st_mode);
173 else
174 handleLastErrorImpl(_path);
175 return false;
176}
177
178
179bool FileImpl::isLinkImpl() const
180{
181 poco_assert (!_path.empty());
182
183 struct stat st;
184 if (lstat(_path.c_str(), &st) == 0)
185 return S_ISLNK(st.st_mode);
186 else
187 handleLastErrorImpl(_path);
188 return false;
189}
190
191
192bool FileImpl::isDeviceImpl() const
193{
194 poco_assert (!_path.empty());
195
196 struct stat st;
197 if (stat(_path.c_str(), &st) == 0)
198 return S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode);
199 else
200 handleLastErrorImpl(_path);
201 return false;
202}
203
204
205bool FileImpl::isHiddenImpl() const
206{
207 poco_assert (!_path.empty());
208 Path p(_path);
209 p.makeFile();
210
211 return p.getFileName()[0] == '.';
212}
213
214
215Timestamp FileImpl::createdImpl() const
216{
217 poco_assert (!_path.empty());
218// first, platforms with birthtime attributes
219#if defined(__APPLE__) && defined(st_birthtime) && !defined(POCO_NO_STAT64) // st_birthtime is available only on 10.5
220 // a macro st_birthtime makes sure there is a st_birthtimespec (nano sec precision)
221 struct stat64 st;
222 if (stat64(_path.c_str(), &st) == 0)
223 return Timestamp(timespec2Microsecs(st.st_birthtimespec));
224#elif defined(__FreeBSD__) && defined(st_birthtime)
225 // a macro st_birthtime makes sure there is a st_birthtimespec (nano sec precision)
226 struct stat st;
227 if (stat(_path.c_str(), &st) == 0)
228 return Timestamp(timespec2Microsecs(st.st_birthtimespec));
229#elif defined(__FreeBSD__)
230 struct stat st;
231 if (stat(_path.c_str(), &st) == 0)
232 return Timestamp::fromEpochTime(st.st_birthtime);
233// then platforms with POSIX 2008-09 compatibility (nanosec precision)
234// (linux 2.6 and later)
235#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) \
236 || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700)
237 struct stat st;
238 if (stat(_path.c_str(), &st) == 0)
239 return Timestamp(timespec2Microsecs(st.st_ctim));
240// finally try just stat with status change with precision to the second.
241#else
242 struct stat st;
243 if (stat(_path.c_str(), &st) == 0)
244 return Timestamp::fromEpochTime(st.st_ctime);
245#endif
246 else
247 handleLastErrorImpl(_path);
248 return 0;
249}
250
251
252Timestamp FileImpl::getLastModifiedImpl() const
253{
254 poco_assert (!_path.empty());
255
256 struct stat st;
257 if (stat(_path.c_str(), &st) == 0) {
258#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) \
259 || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) \
260 || defined(_BSD_SOURCE) || defined(_SVID_SOURCE)
261 return Timestamp(timespec2Microsecs(st.st_mtim));
262#elif defined(__APPLE__)
263 return Timestamp(timespec2Microsecs(st.st_mtimespec));
264#else
265 return Timestamp::fromEpochTime(st.st_mtime);
266#endif
267 }
268 else
269 handleLastErrorImpl(_path);
270 return 0;
271}
272
273
274void FileImpl::setLastModifiedImpl(const Timestamp& ts)
275{
276 poco_assert (!_path.empty());
277
278 struct timeval tb[2];
279 tb[0].tv_sec = ts.epochMicroseconds() / 1000000;
280 tb[0].tv_usec = ts.epochMicroseconds() % 1000000;
281 tb[1] = tb[0];
282 if (utimes(_path.c_str(), tb) != 0)
283 handleLastErrorImpl(_path);
284}
285
286
287FileImpl::FileSizeImpl FileImpl::getSizeImpl() const
288{
289 poco_assert (!_path.empty());
290
291 struct stat st;
292 if (stat(_path.c_str(), &st) == 0)
293 return st.st_size;
294 else
295 handleLastErrorImpl(_path);
296 return 0;
297}
298
299
300void FileImpl::setSizeImpl(FileSizeImpl size)
301{
302 poco_assert (!_path.empty());
303
304 if (truncate(_path.c_str(), size) != 0)
305 handleLastErrorImpl(_path);
306}
307
308
309void FileImpl::setWriteableImpl(bool flag)
310{
311 poco_assert (!_path.empty());
312
313 struct stat st;
314 if (stat(_path.c_str(), &st) != 0)
315 handleLastErrorImpl(_path);
316 mode_t mode;
317 if (flag)
318 {
319 mode = st.st_mode | S_IWUSR;
320 }
321 else
322 {
323 mode_t wmask = S_IWUSR | S_IWGRP | S_IWOTH;
324 mode = st.st_mode & ~wmask;
325 }
326 if (chmod(_path.c_str(), mode) != 0)
327 handleLastErrorImpl(_path);
328}
329
330
331void FileImpl::setExecutableImpl(bool flag)
332{
333 poco_assert (!_path.empty());
334
335 struct stat st;
336 if (stat(_path.c_str(), &st) != 0)
337 handleLastErrorImpl(_path);
338 mode_t mode;
339 if (flag)
340 {
341 mode = st.st_mode | S_IXUSR;
342 if (st.st_mode & S_IRGRP)
343 mode |= S_IXGRP;
344 if (st.st_mode & S_IROTH)
345 mode |= S_IXOTH;
346 }
347 else
348 {
349 mode_t wmask = S_IXUSR | S_IXGRP | S_IXOTH;
350 mode = st.st_mode & ~wmask;
351 }
352 if (chmod(_path.c_str(), mode) != 0)
353 handleLastErrorImpl(_path);
354}
355
356
357void FileImpl::copyToImpl(const std::string& path) const
358{
359 poco_assert (!_path.empty());
360
361 int sd = open(_path.c_str(), O_RDONLY);
362 if (sd == -1) handleLastErrorImpl(_path);
363
364 struct stat st;
365 if (fstat(sd, &st) != 0)
366 {
367 close(sd);
368 handleLastErrorImpl(_path);
369 }
370 const long blockSize = st.st_blksize;
371
372 int dd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, st.st_mode);
373 if (dd == -1)
374 {
375 close(sd);
376 handleLastErrorImpl(path);
377 }
378 Buffer<char> buffer(blockSize);
379 try
380 {
381 int n;
382 while ((n = read(sd, buffer.begin(), blockSize)) > 0)
383 {
384 if (write(dd, buffer.begin(), n) != n)
385 handleLastErrorImpl(path);
386 }
387 if (n < 0)
388 handleLastErrorImpl(_path);
389 }
390 catch (...)
391 {
392 close(sd);
393 close(dd);
394 throw;
395 }
396 close(sd);
397 if (fsync(dd) != 0)
398 {
399 close(dd);
400 handleLastErrorImpl(path);
401 }
402 if (close(dd) != 0)
403 handleLastErrorImpl(path);
404}
405
406
407void FileImpl::renameToImpl(const std::string& path)
408{
409 poco_assert (!_path.empty());
410
411 if (rename(_path.c_str(), path.c_str()) != 0)
412 handleLastErrorImpl(_path);
413}
414
415
416void FileImpl::removeImpl()
417{
418 poco_assert (!_path.empty());
419
420 int rc;
421 if (!isLinkImpl() && isDirectoryImpl())
422 rc = rmdir(_path.c_str());
423 else
424 rc = unlink(_path.c_str());
425 if (rc) handleLastErrorImpl(_path);
426}
427
428
429bool FileImpl::createFileImpl()
430{
431 poco_assert (!_path.empty());
432
433 int n = open(_path.c_str(), O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
434 if (n != -1)
435 {
436 close(n);
437 return true;
438 }
439 if (n == -1 && errno == EEXIST)
440 return false;
441 else
442 handleLastErrorImpl(_path);
443 return false;
444}
445
446
447bool FileImpl::createDirectoryImpl()
448{
449 poco_assert (!_path.empty());
450
451 if (existsImpl() && isDirectoryImpl())
452 return false;
453 if (mkdir(_path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) != 0)
454 {
455 if (errno == EEXIST && isDirectoryImpl())
456 return false;
457 handleLastErrorImpl(_path);
458 }
459 return true;
460}
461
462
463FileImpl::FileSizeImpl FileImpl::totalSpaceImpl() const
464{
465 poco_assert(!_path.empty());
466
467 struct STATFSSTRUCT stats;
468 if (STATFSFN(const_cast<char*>(_path.c_str()), &stats) != 0)
469 handleLastErrorImpl(_path);
470
471 return (FileSizeImpl)stats.f_blocks * (FileSizeImpl)stats.f_bsize;
472}
473
474
475FileImpl::FileSizeImpl FileImpl::usableSpaceImpl() const
476{
477 poco_assert(!_path.empty());
478
479 struct STATFSSTRUCT stats;
480 if (STATFSFN(const_cast<char*>(_path.c_str()), &stats) != 0)
481 handleLastErrorImpl(_path);
482
483 return (FileSizeImpl)stats.f_bavail * (FileSizeImpl)stats.f_bsize;
484}
485
486
487FileImpl::FileSizeImpl FileImpl::freeSpaceImpl() const
488{
489 poco_assert(!_path.empty());
490
491 struct STATFSSTRUCT stats;
492 if (STATFSFN(const_cast<char*>(_path.c_str()), &stats) != 0)
493 handleLastErrorImpl(_path);
494
495 return (FileSizeImpl)stats.f_bfree * (FileSizeImpl)stats.f_bsize;
496}
497
498
499void FileImpl::handleLastErrorImpl(const std::string& path)
500{
501 switch (errno)
502 {
503 case EIO:
504 throw IOException(path, errno);
505 case EPERM:
506 throw FileAccessDeniedException("insufficient permissions", path, errno);
507 case EACCES:
508 throw FileAccessDeniedException(path, errno);
509 case ENOENT:
510 throw FileNotFoundException(path, errno);
511 case ENOTDIR:
512 throw OpenFileException("not a directory", path, errno);
513 case EISDIR:
514 throw OpenFileException("not a file", path, errno);
515 case EROFS:
516 throw FileReadOnlyException(path, errno);
517 case EEXIST:
518 throw FileExistsException(path, errno);
519 case ENOSPC:
520 throw FileException("no space left on device", path, errno);
521 case EDQUOT:
522 throw FileException("disk quota exceeded", path, errno);
523#if !defined(_AIX)
524 case ENOTEMPTY:
525 throw DirectoryNotEmptyException(path, errno);
526#endif
527 case ENAMETOOLONG:
528 throw PathSyntaxException(path, errno);
529 case ENFILE:
530 case EMFILE:
531 throw FileException("too many open files", path, errno);
532 default:
533 throw FileException(std::strerror(errno), path, errno);
534 }
535}
536
537
538} // namespace Poco
539