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