45 #include <sys/types.h>
47 #include <sys/mount.h>
48 #if defined(__linux__)
49 #include <sys/sendfile.h>
50 #include <linux/loop.h>
66#if defined(__linux__) && defined(__GLIBC__)
68 #define _USE_SENDFILE_ 1
69#elif defined(__linux__)
70 #define _USE_SENDFILE_ 1
73#if defined(__FreeBSD__)
75 typedef off_t off64_t;
76 #define __posix_fstatat64 ::fstatat
77 #define __posix_openat64 ::openat
80 #define __posix_fstatat64 ::fstatat64
81 #define __posix_openat64 ::openat64
85 const size_t bsz = PATH_MAX;
90 char* res = ::getcwd(&str[0], bsz);
91 if( res == &str[0] ) {
92 str.resize(::strnlen(res, bsz));
101 return 0 ==
::chdir(path.c_str());
105 const size_t bsz = PATH_MAX;
110 char *res = ::realpath(&relpath[0], &str[0]);
111 if( res == &str[0] ) {
112 str.resize(::strnlen(res, bsz));
116 return std::string();
132 if( 0 == path.size() ) {
136 if(
c_slash == path[path.size()-1] ) {
137 if( 1 == path.size() ) {
138 return std::string(path);
140 end_pos = path.size()-2;
142 end_pos = path.size()-1;
144 size_t idx = path.find_last_of(
c_slash, end_pos);
145 if( idx == std::string_view::npos ) {
149 return std::string( path.substr(0, std::max<size_t>(1, idx)) );
154 if( 0 == path.size() ) {
158 if(
c_slash == path[path.size()-1] ) {
159 if( 1 == path.size() ) {
160 return std::string(path);
162 end_pos = path.size()-2;
164 end_pos = path.size()-1;
166 size_t idx = path.find_last_of(
c_slash, end_pos);
167 if( idx == std::string_view::npos ) {
168 return std::string( path.substr(0, end_pos+1));
170 return std::string( path.substr(idx+1, end_pos-idx) );
175 return path.size() > 0 &&
181 ::memset(&s, 0,
sizeof(s));
184 if( 0 != stat_res && verbose_on_error ) {
185 fprintf(stderr,
"exists '%s': %d: %d %s\n", path.c_str(), stat_res, errno, strerror(errno));
187 return 0 == stat_res;
195 std::string assetdir0 =
"resources";
196 if(
exists( assetdir0+
"/"+asset_file ) ) {
199 if( !exe_path || !asset_install_subdir ) {
206 std::string assetdir1 =
jau::fs::absolute( adir+
"/../share/"+asset_install_subdir );
207 if(
exists( assetdir1+
"/"+asset_file ) ) {
210 fprintf(stderr,
"asset_dir: Not found: dir '%s', file '%s', exe[path '%s', dir '%s'], cwd '%s', adir '%s'\n",
211 assetdir1.c_str(), asset_file, exe_path, exedir.c_str(), cwd.c_str(), adir.c_str());
215std::unique_ptr<dir_item::backed_string_view> dir_item::reduce(
const std::string_view& path_)
noexcept {
216 constexpr const bool _debug =
false;
218 if constexpr ( _debug ) {
219 jau::fprintf_td(stderr,
"X.0: path '%s'\n", std::string(path_).c_str());
222 std::unique_ptr<dir_item::backed_string_view> path2 = std::make_unique<dir_item::backed_string_view>(path_);
230 path2->view = path2->view.substr(2, path2->view.size()-2);
234 if(
c_slash == path2->view[path2->view.size()-1] &&
235 ( path2->view.size() < 3 || std::string_view::npos == path2->view.find(
s_slash_dot_slash, path2->view.size()-3) ) &&
236 ( path2->view.size() < 4 || std::string_view::npos == path2->view.find(
s_slash_dotdot_slash, path2->view.size()-4) )
239 path2->view = path2->view.substr(0, path2->view.size()-1);
243 if( ( path2->view.size() >= 3 && std::string_view::npos != path2->view.find(
s_slash_dotdot, path2->view.size()-3) ) ||
244 ( path2->view.size() >= 2 && std::string_view::npos != path2->view.find(
s_slash_dot, path2->view.size()-2) ) ) {
246 path2->view = path2->backing.append(
s_slash);
249 if constexpr ( _debug ) {
250 jau::fprintf_td(stderr,
"X.1: path2 '%s'\n", path2->to_string(
true).c_str());
258 if constexpr ( _debug ) {
259 jau::fprintf_td(stderr,
"X.2.1: path2: spos %zu, idx %zu, '%s'\n", spos, idx, path2->to_string(
true).c_str());
261 if( std::string_view::npos == idx ) {
264 std::string_view pre = path2->view.substr(0, idx);
265 if( 0 == pre.size() ) {
267 path2->view = path2->view.substr(idx+2);
271 const std::string post( path2->view.substr(idx+2) );
272 path2->backup_and_append( pre, post );
275 if constexpr ( _debug ) {
276 jau::fprintf_td(stderr,
"X.2.2: path2: spos %zu, '%s'\n", spos, path2->to_string(
true).c_str());
278 }
while( spos <= path2->view.size()-3 );
279 if constexpr ( _debug ) {
280 jau::fprintf_td(stderr,
"X.2.X: path2: '%s'\n", path2->to_string(
true).c_str());
287 if constexpr ( _debug ) {
288 jau::fprintf_td(stderr,
"X.3.1: path2: spos %zu, idx %zu, '%s'\n", spos, idx, path2->to_string(
true).c_str());
290 if( std::string_view::npos == idx ) {
295 WARN_PRINT(
"dir_item::resolve: '..' resolution error: '%s' -> '%s'", std::string(path_).c_str(), path2->to_string().c_str());
298 std::string_view pre = path2->view.substr(0, idx);
302 }
else if( 3 <= idx &&
s_slash_dotdot == path2->view.substr(idx-3, 3) ) {
309 path2->view = path2->view.substr(idx+3);
311 }
else if(
s_dot == pre_str ) {
313 path2->view = path2->view.substr(idx+4);
317 const std::string post( path2->view.substr(idx+3) );
318 path2->backup_and_append( pre_str, post );
319 spos = pre_str.size();
322 if constexpr ( _debug ) {
323 jau::fprintf_td(stderr,
"X.3.2: path2: spos %zu, '%s'\n", spos, path2->to_string(
true).c_str());
325 }
while( spos <= path2->view.size()-4 );
326 if constexpr ( _debug ) {
327 jau::fprintf_td(stderr,
"X.3.X: path2: '%s'\n", path2->to_string(
true).c_str());
331 if(
c_slash == path2->view[path2->view.size()-1] ) {
332 path2->view = path2->view.substr(0, path2->view.size()-1);
334 if constexpr ( _debug ) {
335 jau::fprintf_td(stderr,
"X.X: path2: '%s'\n", path2->to_string(
true).c_str());
348: dirname_(std::move(dirname__)), basename_(std::move(basename__)), empty_(dirname_.empty() && basename_.empty()) {
352: dirname_(
s_dot), basename_(
s_dot), empty_(true) {}
355: dir_item( reduce(path_) )
360 if(
s_dot == dirname_ ) {
363 if(
s_dot == basename_ ) {
367 return dirname_ + basename_;
369 return dirname_ +
s_slash + basename_;
378 if(
is_set( mask, bit )) {
395 const std::string r(
"r");
396 const std::string w(
"w");
397 const std::string x(
"x");
410 out.append(std::string(buf, len));
420 std::string res(
"/dev/fd/");
421 res.append(std::to_string(fd));
427 if( 1 == sscanf(named_fd.c_str(),
"/dev/fd/%d", &scan_value) ) {
430 }
else if( 1 == sscanf(named_fd.c_str(),
"/proc/self/fd/%d", &scan_value) ) {
439 uid_(0), gid_(0), size_(0), btime_(), atime_(), ctime_(), mtime_(),
444 static constexpr bool jau_has_stat(
const uint32_t mask,
const uint32_t bit) {
return bit == ( mask & bit ); }
449 uid_(0), gid_(0), size_(0), btime_(), atime_(), ctime_(), mtime_(), errno_res_(0)
451 constexpr const bool _debug =
false;
453 const std::string full_path(
item.empty() ?
"" :
item.path() );
454 if(
item.empty() && AT_FDCWD != dirfd ) {
460 ERR_PRINT(
"rec_level %d, dirfd %d < 0, %s, dirfd_is_item_dirname %d, AT_EMPTY_PATH",
461 (
int)cc.rec_level, dirfd,
item.to_string().c_str(), dirfd_is_item_dirname);
467 if( 0 <= scan_value ) {
471 }
else if( full_path.starts_with(
"/dev/fd/pipe:") ) {
479 if constexpr ( _debug ) {
480 jau::fprintf_td(stderr,
"file_stats(%d): FIFO: '%s', errno %d (%s)\n", (
int)cc.rec_level,
to_string().c_str(), errno, ::strerror(errno));
485 const std::string dirfd_path =
has(
field_t::fd ) ?
"" : ( dirfd_is_item_dirname ? item_.basename() : full_path );
490 int stat_res = ::statx(dirfd, dirfd_path.c_str(),
491 ( AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | (
has(
field_t::fd ) ? AT_EMPTY_PATH : 0 ) ),
492 ( STATX_BASIC_STATS | STATX_BTIME ), &s);
493 if( 0 != stat_res ) {
494 if constexpr ( _debug ) {
495 jau::fprintf_td(stderr,
"file_stats(%d): Test ERROR: '%s', %d, errno %d (%s)\n", (
int)cc.rec_level, full_path.c_str(), stat_res, errno, ::strerror(errno));
511 if( jau_has_stat( s.stx_mask, STATX_TYPE ) ) {
515 if( S_ISLNK( s.stx_mode ) ) {
518 if( S_ISREG( s.stx_mode ) ) {
520 }
else if( S_ISDIR( s.stx_mode ) ) {
522 }
else if( S_ISFIFO( s.stx_mode ) ) {
524 }
else if( S_ISCHR( s.stx_mode ) ) {
526 }
else if( S_ISSOCK( s.stx_mode ) ) {
528 }
else if( S_ISBLK( s.stx_mode ) ) {
532 if( jau_has_stat( s.stx_mask, STATX_MODE ) ) {
535 mode_ |=
static_cast<fmode_t>( s.stx_mode & ( S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX ) );
537 if( jau_has_stat( s.stx_mask, STATX_NLINK ) ) {
540 if( jau_has_stat( s.stx_mask, STATX_UID ) ) {
544 if( jau_has_stat( s.stx_mask, STATX_GID ) ) {
548 if( jau_has_stat( s.stx_mask, STATX_ATIME ) || 0 != s.stx_atime.tv_sec || 0 != s.stx_atime.tv_nsec ) {
552 if( jau_has_stat( s.stx_mask, STATX_MTIME ) ) {
556 if( jau_has_stat( s.stx_mask, STATX_CTIME ) ) {
560 if( jau_has_stat( s.stx_mask, STATX_INO ) ) {
563 if( jau_has_stat( s.stx_mask, STATX_SIZE ) ) {
569 if( jau_has_stat( s.stx_mask, STATX_BLOCKS ) ) {
572 if( jau_has_stat( s.stx_mask, STATX_BTIME ) ) {
578 std::string link_path;
580 const size_t path_link_max_len = 0 < s.stx_size ? s.stx_size + 1 : PATH_MAX;
581 std::vector<char> buffer;
582 buffer.reserve(path_link_max_len);
583 buffer.resize(path_link_max_len);
584 const ssize_t path_link_len = ::readlinkat(dirfd, dirfd_path.c_str(), buffer.data(), path_link_max_len);
585 if( 0 > path_link_len ) {
587 link_target_ = std::make_shared<file_stats>();
591 link_path = std::string(buffer.data(), path_link_len);
593 link_target_path_ = std::make_shared<std::string>(link_path);
594 if( 0 == cc.rec_level ) {
597 stat_res = ::statx(dirfd, dirfd_path.c_str(), AT_NO_AUTOMOUNT | (
has(
field_t::fd ) ? AT_EMPTY_PATH : 0 ), STATX_BASIC_STATS, &s);
598 if( 0 != stat_res ) {
599 if constexpr ( _debug ) {
600 jau::fprintf_td(stderr,
"file_stats(%d): Test link ERROR: '%s', %d, errno %d (%s)\n", (
int)cc.rec_level, full_path.c_str(), stat_res, errno, ::strerror(errno));
620 if( 0 < link_path.size() &&
c_slash == link_path[0] ) {
621 link_target_ = std::make_shared<file_stats>(ctor_cookie(cc.rec_level+1), dirfd,
dir_item( link_path ),
false );
623 link_target_ = std::make_shared<file_stats>(ctor_cookie(cc.rec_level+1), dirfd,
dir_item(
jau::fs::dirname(full_path), link_path ), dirfd_is_item_dirname );
625 if( link_target_->has_fd() ) {
627 fd_ = link_target_->fd();
629 if( link_target_->is_socket() ) {
631 }
else if( link_target_->is_block() ) {
633 }
else if( link_target_->is_char() ) {
635 }
else if( link_target_->is_fifo() ) {
637 }
else if( link_target_->is_dir() ) {
639 }
else if( link_target_->is_file() ) {
643 size_ = link_target_->size();
645 }
else if( !link_target_->exists() ) {
647 }
else if( !link_target_->has_access() ) {
651 if constexpr ( _debug ) {
652 jau::fprintf_td(stderr,
"file_stats(%d): '%s', %d, errno %d (%s)\n", (
int)cc.rec_level,
to_string().c_str(), stat_res, errno, ::strerror(errno));
659 if( 0 != stat_res ) {
660 if constexpr ( _debug ) {
661 jau::fprintf_td(stderr,
"file_stats(%d): Test ERROR: '%s', %d, errno %d (%s)\n", (
int)cc.rec_level, full_path.c_str(), stat_res, errno, ::strerror(errno));
680 if( S_ISLNK( s.st_mode ) ) {
683 if( S_ISREG( s.st_mode ) ) {
689 }
else if( S_ISDIR( s.st_mode ) ) {
691 }
else if( S_ISFIFO( s.st_mode ) ) {
693 }
else if( S_ISCHR( s.st_mode ) ) {
695 }
else if( S_ISSOCK( s.st_mode ) ) {
697 }
else if( S_ISBLK( s.st_mode ) ) {
702 mode_ |=
static_cast<fmode_t>( s.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX ) );
712 std::string link_path;
714 const size_t path_link_max_len = 0 < s.st_size ? s.st_size + 1 : PATH_MAX;
715 std::vector<char> buffer;
716 buffer.reserve(path_link_max_len);
717 buffer.resize(path_link_max_len);
718 const ssize_t path_link_len = ::readlinkat(dirfd, dirfd_path.c_str(), buffer.data(), path_link_max_len);
719 if( 0 > path_link_len ) {
721 link_target_ = std::make_shared<file_stats>();
725 link_path = std::string(buffer.data(), path_link_len);
727 link_target_path_ = std::make_shared<std::string>(link_path);
728 if( 0 == cc.rec_level ) {
732 if( 0 != stat_res ) {
733 if constexpr ( _debug ) {
734 jau::fprintf_td(stderr,
"file_stats(%d): Test link ERROR: '%s', %d, errno %d (%s)\n", (
int)cc.rec_level, full_path.c_str(), stat_res, errno, ::strerror(errno));
754 if( 0 < link_path.size() &&
c_slash == link_path[0] ) {
755 link_target_ = std::make_shared<file_stats>(ctor_cookie(cc.rec_level+1), dirfd,
dir_item( link_path ),
false );
757 link_target_ = std::make_shared<file_stats>(ctor_cookie(cc.rec_level+1), dirfd,
dir_item(
jau::fs::dirname(full_path), link_path ), dirfd_is_item_dirname );
759 if( link_target_->has_fd() ) {
761 fd_ = link_target_->fd();
763 if( link_target_->is_socket() ) {
765 }
else if( link_target_->is_block() ) {
767 }
else if( link_target_->is_char() ) {
769 }
else if( link_target_->is_fifo() ) {
771 }
else if( link_target_->is_dir() ) {
773 }
else if( link_target_->is_file() ) {
776 size_ = link_target_->size();
777 }
else if( !link_target_->exists() ) {
779 }
else if( !link_target_->has_access() ) {
783 if constexpr ( _debug ) {
784 jau::fprintf_td(stderr,
"file_stats(%d): '%s', %d, errno %d (%s)\n", (
int)cc.rec_level,
to_string().c_str(), stat_res, errno, ::strerror(errno));
816 while(
nullptr != fs1 ) {
821 if(
nullptr != link_count ) {
835 return item_ == rhs.item_ &&
836 has_fields_ == rhs.has_fields_ &&
837 mode_ == rhs.mode_ &&
838 uid_ == rhs.uid_ && gid_ == rhs.gid_ &&
839 errno_res_ == rhs.errno_res_ &&
840 size_ == rhs.size_ &&
841 btime_ == rhs.btime_ &&
842 atime_ == rhs.atime_ &&
843 ctime_ == rhs.ctime_ &&
844 mtime_ == rhs.mtime_ &&
846 ( link_target_path_ == rhs.link_target_path_&&
847 link_target_ == rhs.link_target_
853 std::string stored_path, link_detail;
855 if(
nullptr != link_target_path_ ) {
856 stored_path =
" [-> "+*link_target_path_+
"]";
860 if( 0 < link_count ) {
861 link_detail =
" -(" + std::to_string(link_count) +
")-> '" + final_target_->
path() +
"'";
864 std::string res(
"file_stats[");
866 .append(
", '"+item_.path()+
"'"+stored_path+link_detail );
867 if( 0 == errno_res_ ) {
869 res.append(
", fd " ).append( std::to_string(fd_) );
872 res.append(
", uid " ).append( std::to_string(uid_) );
875 res.append(
", gid " ).append( std::to_string(gid_) );
880 res.append(
", size n/a" );
883 res.append(
", btime " ).append( btime_.to_iso8601_string() );
886 res.append(
", atime " ).append( atime_.to_iso8601_string() );
889 res.append(
", ctime " ).append( ctime_.to_iso8601_string() );
892 res.append(
", mtime " ).append( mtime_.to_iso8601_string() );
896 res.append(
", errno " ).append( std::to_string(errno_res_) ).append(
", " ).append( std::string(::strerror(errno_res_)) );
910 }
else if( !stats.
exists() ) {
912 if ( 0 != dir_err ) {
927 ERR_PRINT(
"Couldn't open/create file '%s'", path.c_str());
930 struct timespec ts2[2] = { atime.to_timespec(), mtime.to_timespec() };
932 if( 0 != ::futimens(fd, ts2) ) {
933 ERR_PRINT(
"Couldn't update time of file '%s'", path.c_str());
945 ERR_PRINT(
"Couldn't open/create file '%s'", path.c_str());
949 if( 0 != ::futimens(fd,
nullptr ) ) {
950 ERR_PRINT(
"Couldn't update time of file '%s'", path.c_str());
963 if( (
dir = ::opendir( path.c_str() ) ) !=
nullptr ) {
964 while ( ( ent = ::readdir(
dir ) ) !=
nullptr ) {
965 std::string fname( ent->d_name );
980 int dirfd2 = ::dup(dirfd);
982 ERR_PRINT(
"Couldn't duplicate given dirfd %d for path '%s'", dirfd, path.c_str());
985 if( (
dir = ::fdopendir( dirfd2 ) ) !=
nullptr ) {
986 while ( ( ent = ::readdir(
dir ) ) !=
nullptr ) {
987 std::string fname( ent->d_name );
1004 const size_t depth = dirfds.size();
1005 if( item_stats.is_dir() ) {
1012 if( dirfds.size() < 1 ) {
1013 ERR_PRINT(
"dirfd stack error: count %zu] @ %s", dirfds.size(), item_stats.to_string().c_str());
1016 const int parent_dirfd = dirfds.back();
1018 if ( 0 > this_dirfd ) {
1019 ERR_PRINT(
"entered path dir couldn't be opened, source %s", item_stats.to_string().c_str());
1022 dirfds.push_back(this_dirfd);
1026 ::close(this_dirfd);
1033 ::close(this_dirfd);
1038 std::vector<dir_item> content;
1040 ( void(*)(std::vector<dir_item>*,
const dir_item&) )
1041 ( [](std::vector<dir_item>* receiver,
const dir_item& item) ->
void { receiver->push_back( item ); } )
1043 if(
get_dir_content(this_dirfd, item_stats.path(), cs) && content.size() > 0 ) {
1047 for (
const dir_item& element : content) {
1048 const file_stats element_stats( this_dirfd, element,
true );
1049 if( element_stats.
is_dir() ) {
1052 ::close(this_dirfd);
1056 }
else if( !
_visit(element_stats, topts, visitor, dirfds) ) {
1057 ::close(this_dirfd);
1063 element_stats, depth ) )
1065 ::close(this_dirfd);
1071 if( dirfds.size() < 2 ) {
1072 ERR_PRINT(
"dirfd stack error: count %zu] @ %s", dirfds.size(), item_stats.to_string().c_str());
1079 ::close(this_dirfd);
1083 else if( item_stats.is_file() || !item_stats.ok() ) {
1092 const bool user_dirfds =
nullptr != dirfds;
1093 if( !user_dirfds ) {
1095 dirfds =
new std::vector<int>();
1096 }
catch (
const std::bad_alloc &e) {
1097 ABORT(
"Error: bad_alloc: dirfds allocation failed");
1101 if( 0 != dirfds->size() ) {
1102 ERR_PRINT(
"dirfd stack error: count %zu @ %s", dirfds->size(), item_stats.to_string().c_str());
1108 ERR_PRINT(
"path dirname couldn't be opened, source %s", item_stats.to_string().c_str());
1111 dirfds->push_back(dirfd);
1113 bool res =
_visit(item_stats, topts, visitor, *dirfds);
1115 if( dirfds->size() != 1 && res ) {
1116 ERR_PRINT(
"dirfd stack error: count %zu", dirfds->size());
1119 while( !dirfds->empty() ) {
1120 ::close(dirfds->back());
1123 if( !user_dirfds ) {
1138 if( !path_stats.
exists() ) {
1144 if( path_stats.
has_fd() ) {
1154 int res = ::unlink( path_stats.
path().c_str() );
1164 if( !path_stats.
is_dir() ) {
1165 ERR_PRINT(
"remove: Error: path is neither file nor dir: %s\n", path_stats.
to_string().c_str());
1175 struct remove_context_t {
1177 std::vector<int> dirfds;
1193 const int dirfd = ctx_ptr->dirfds.back();
1194 const std::string& basename_ = element_stats.
item().
basename();
1198 const int dirfd2 = *( ctx_ptr->dirfds.end() - 2 );
1199 const int res = ::unlinkat( dirfd2, basename_.c_str(), AT_REMOVEDIR );
1208 const int res = ::unlinkat( dirfd, basename_.c_str(), 0 );
1229 if( !source1.is_file() ) {
1230 ERR_PRINT(
"source1_stats is not a file: %s", source1.to_string().c_str());
1233 if( !source2.is_file() ) {
1234 ERR_PRINT(
"source2_stats is not a file: %s", source2.to_string().c_str());
1238 if( source1.size() != source2.size() ) {
1240 jau::fprintf_td(stderr,
"compare: Source files size mismatch, %s != %s\n",
1241 source1.to_string().c_str(), source2.to_string().c_str());
1246 int src1=-1, src2=-1;
1247 int src_flags = O_RDONLY|
O_BINARY|O_NOCTTY;
1248 uint64_t offset = 0;
1253 ERR_PRINT(
"Failed to open source1 %s, errno %d, %s", source1.to_string().c_str());
1258 ERR_PRINT(
"Failed to open source2 %s, errno %d, %s", source2.to_string().c_str());
1261 while ( offset < source1.size()) {
1263 char buffer1[BUFSIZ];
1264 char buffer2[BUFSIZ];
1266 if( ( rc1 = ::read(src1, buffer1,
sizeof(buffer1)) ) > 0 ) {
1267 ssize_t bytes_to_write = rc1;
1268 size_t buffer_offset = 0;
1269 while( 0 <= rc2 && 0 < bytes_to_write ) {
1270 while( ( rc2 = ::read(src2, buffer2+buffer_offset, bytes_to_write) ) < 0 ) {
1271 if ( errno == EAGAIN || errno == EINTR ) {
1277 buffer_offset += rc2;
1278 bytes_to_write -= rc2;
1279 offset += (uint64_t)rc2;
1281 }
else if ( 0 > rc1 && ( errno == EAGAIN || errno == EINTR ) ) {
1285 if ( 0 > rc1 || 0 > rc2 ) {
1287 ERR_PRINT(
"Failed to read source1 bytes @ %s / %s, %s",
1289 source1.to_string().c_str());
1290 }
else if ( 0 > rc2 ) {
1291 ERR_PRINT(
"Failed to read source2 bytes @ %s / %s, %s",
1293 source2.to_string().c_str());
1297 if( 0 != ::memcmp(buffer1, buffer2, rc1) ) {
1299 jau::fprintf_td(stderr,
"compare: Difference within %s bytes @ %s / %s, %s != %s\n",
1301 source1.to_string().c_str(), source2.to_string().c_str());
1309 if( offset < source1.size() ) {
1310 ERR_PRINT(
"Incomplete transfer %s / %s, %s != %s\n",
1312 source1.to_string().c_str(), source2.to_string().c_str());
1334 const int dst_dirfd,
const std::string& dst_basename,
const copy_options copts)
noexcept {
1335 file_stats dst_stats(dst_dirfd, dst_basename);
1341 jau::fprintf_td(stderr,
"copy: Error: dest_path exists but copy_options::overwrite not set: source %s, dest '%s', copts %s\n",
1342 src_stats.to_string().c_str(), dst_stats.
to_string().c_str(),
to_string( copts ).c_str());
1346 const int res = ::unlinkat(dst_dirfd, dst_basename.c_str(), 0);
1348 ERR_PRINT(
"remove existing dest_path for symbolic-link failed: source %s, dest '%s'",
1349 src_stats.to_string().c_str(), dst_stats.
to_string().c_str());
1356 const std::shared_ptr<std::string>& link_target_path = src_stats.link_target_path();
1357 if(
nullptr == link_target_path || 0 == link_target_path->size() ) {
1358 ERR_PRINT(
"Symbolic link-path is empty %s", src_stats.to_string().c_str());
1362 const int res = ::symlinkat(link_target_path->c_str(), dst_dirfd, dst_basename.c_str());
1366 jau::fprintf_td(stderr,
"copy: Ignored: Failed to create symink %s -> %s, %s, errno %d, %s\n",
1367 dst_basename.c_str(), link_target_path->c_str(), src_stats.to_string().c_str(), errno, ::strerror(errno));
1371 ERR_PRINT(
"Creating symlink failed %s -> %s, %s", dst_basename.c_str(), link_target_path->c_str(), src_stats.to_string().c_str());
1376 struct timespec ts2[2] = { src_stats.atime().to_timespec(), src_stats.mtime().to_timespec() };
1377 if( 0 != ::utimensat(dst_dirfd, dst_basename.c_str(), ts2, AT_SYMLINK_NOFOLLOW) ) {
1378 ERR_PRINT(
"Couldn't preserve time of symlink, source %s, dest '%s'", src_stats.to_string().c_str(), dst_basename.c_str());
1382 const uid_t caller_uid = ::geteuid();
1383 const ::uid_t source_uid = 0 == caller_uid ? src_stats.uid() : -1;
1384 if( 0 != ::fchownat(dst_dirfd, dst_basename.c_str(), source_uid, src_stats.gid(), AT_SYMLINK_NOFOLLOW) ) {
1385 if( errno != EPERM && errno != EINVAL ) {
1386 ERR_PRINT(
"Couldn't preserve ownership of symlink, source %s, dest '%s'", src_stats.to_string().c_str(), dst_basename.c_str());
1391 jau::fprintf_td(stderr,
"copy: Warn: Couldn't preserve ownership of symlink, source %s, dest '%s', errno %d (%s)\n",
1392 src_stats.to_string().c_str(), dst_basename.c_str(), errno, ::strerror(errno));
1403 const uid_t caller_uid = ::geteuid();
1405 int src_flags = O_RDONLY|
O_BINARY|O_NOCTTY;
1406 uint64_t offset = 0;
1409#if defined(__linux__)
1410 if( caller_uid == target_stats->
uid() ) {
1411 src_flags |= O_NOATIME;
1414 src =
__posix_openat64(src_dirfd, src_stats.item().basename().c_str(), src_flags);
1416 if( src_stats.is_link() ) {
1420 ERR_PRINT(
"Failed to open source %s", src_stats.to_string().c_str());
1422 jau::fprintf_td(stderr,
"copy: Ignored: Failed to open source %s, errno %d, %s\n", src_stats.to_string().c_str(), errno, ::strerror(errno));
1428 ERR_PRINT(
"Failed to open target_path '%s'", dst_basename.c_str());
1431 while ( offset < src_stats.size()) {
1433#ifdef _USE_SENDFILE_
1434 off64_t offset_i = (off64_t)offset;
1435 const uint64_t count = std::max<uint64_t>(std::numeric_limits<ssize_t>::max(), src_stats.size() - offset);
1436 if( ( rc1 = ::sendfile64(dst, src, &offset_i, (
size_t)count) ) >= 0 ) {
1437 offset = (uint64_t)offset_i;
1440 char buffer[BUFSIZ];
1441 if( ( rc1 = ::read(src, buffer,
sizeof(buffer)) ) > 0 ) {
1442 ssize_t bytes_to_write = rc1;
1443 size_t buffer_offset = 0;
1444 while( 0 <= rc2 && 0 < bytes_to_write ) {
1445 while( ( rc2 =
::write(dst, buffer+buffer_offset, bytes_to_write) ) < 0 ) {
1446 if ( errno == EAGAIN || errno == EINTR ) {
1452 buffer_offset += rc2;
1453 bytes_to_write -= rc2;
1456 }
else if ( 0 > rc1 && ( errno == EAGAIN || errno == EINTR ) ) {
1461 if ( 0 > rc1 || 0 > rc2 ) {
1462#ifdef _USE_SENDFILE_
1463 ERR_PRINT(
"Failed to copy bytes @ %s / %s, %s -> '%s'",
1465 src_stats.to_string().c_str(), dst_basename.c_str());
1468 ERR_PRINT(
"Failed to read bytes @ %s / %s, %s",
1470 src_stats.to_string().c_str());
1471 }
else if ( 0 > rc2 ) {
1472 ERR_PRINT(
"Failed to write bytes @ %s / %s, %s",
1474 dst_basename.c_str());
1483 if( offset < src_stats.size() ) {
1484 ERR_PRINT(
"Incomplete transfer %s / %s, %s -> '%s'",
1486 src_stats.to_string().c_str(), dst_basename.c_str());
1493 ERR_PRINT(
"Couldn't restore omitted permissions, source %s, dest '%s'",
1494 src_stats.to_string().c_str(), dst_basename.c_str());
1501 if( 0 != ::futimens(dst, ts2) ) {
1502 ERR_PRINT(
"Couldn't preserve time of file, source %s, dest '%s'",
1503 src_stats.to_string().c_str(), dst_basename.c_str());
1507 ::uid_t source_uid = 0 == caller_uid ? target_stats->
uid() : -1;
1508 if( 0 != ::fchown(dst, source_uid, target_stats->
gid()) ) {
1509 if( errno != EPERM && errno != EINVAL ) {
1510 ERR_PRINT(
"Couldn't preserve ownership of file, uid(caller %" PRIu32
", chown %" PRIu32
"), source %s, dest '%s'",
1511 caller_uid, source_uid, src_stats.to_string().c_str(), dst_basename.c_str());
1516 jau::fprintf_td(stderr,
"copy: Ignored: Preserve ownership of file failed, uid(caller %" PRIu32
", chown %" PRIu32
"), source %s, dest '%s', errno %d (%s)\n",
1517 caller_uid, source_uid, src_stats.to_string().c_str(), dst_stats.
to_string().c_str(), errno, ::strerror(errno));
1523 if( 0 != ::fsync(dst) ) {
1524 ERR_PRINT(
"Couldn't synchronize destination file, source %s, dest '%s'",
1525 src_stats.to_string().c_str(), dst_basename.c_str());
1543 bool new_dir =
false;
1544 std::string basename_;
1545 const int dest_dirfd = ctx.dst_dirfds.back();
1546 if( dst_stats.is_dir() ) {
1548 jau::fprintf_td(stderr,
"copy: mkdir directory already exist: %s\n", dst_stats.to_string().c_str());
1550 basename_.append( dst_stats.item().basename() );
1551 }
else if( !dst_stats.exists() ) {
1553 constexpr const int32_t val_min = 888;
1554 constexpr const int32_t val_max = std::numeric_limits<int32_t>::max();
1555 uint64_t mkdir_cntr = 0;
1556 std::mt19937_64 prng;
1557 std::uniform_int_distribution<int32_t> prng_dist(val_min, val_max);
1558 bool mkdir_ok =
false;
1561 const int32_t val_d = prng_dist(prng);
1566 }
else if (errno != EINTR && errno != EEXIST) {
1567 ERR_PRINT(
"mkdir failed: %s, temp '%s'", dst_stats.to_string().c_str(), basename_.c_str());
1570 }
while( !mkdir_ok && mkdir_cntr < val_max );
1572 ERR_PRINT(
"mkdir failed: %s", dst_stats.to_string().c_str());
1576 ERR_PRINT(
"mkdir failed: %s, exists but is no dir", dst_stats.to_string().c_str());
1581 if ( 0 > new_dirfd ) {
1583 ERR_PRINT(
"Couldn't open new dir %s, temp '%s'", dst_stats.to_string().c_str(), basename_.c_str());
1585 ERR_PRINT(
"Couldn't open new dir %s", dst_stats.to_string().c_str());
1588 ::unlinkat(dest_dirfd, basename_.c_str(), AT_REMOVEDIR);
1595 ::unlinkat(dest_dirfd, basename_.c_str(), AT_REMOVEDIR);
1596 ERR_PRINT(
"zero permissions on dest %s, temp '%s'", dst_stats.to_string().c_str(), basename_.c_str());
1598 ERR_PRINT(
"zero permissions on dest %s", dst_stats.to_string().c_str());
1604#if defined(__linux__) && defined(__GLIBC__)
1605 const int rename_flags = 0;
1606 const int rename_res = ::renameat2(dest_dirfd, basename_.c_str(), dest_dirfd, dst_stats.item().basename().c_str(), rename_flags);
1608 const int rename_res = ::renameat(dest_dirfd, basename_.c_str(), dest_dirfd, dst_stats.item().basename().c_str());
1610 if( 0 != rename_res ) {
1611 ERR_PRINT(
"rename temp to dest, temp '%s', dest %s", basename_.c_str(), dst_stats.to_string().c_str());
1612 ::unlinkat(dest_dirfd, basename_.c_str(), AT_REMOVEDIR);
1617 ctx.dst_dirfds.push_back(new_dirfd);
1622 const file_stats* target_stats = src_stats.
is_link() ? src_stats.link_target().get() : &src_stats;
1627 ERR_PRINT(
"restore permissions, source %s, dest '%s'", src_stats.to_string().c_str(), dst_basename.c_str());
1634 if( 0 != ::futimens(dst_dirfd, ts2) ) {
1635 ERR_PRINT(
"preserve time of file failed, source %s, dest '%s'", src_stats.to_string().c_str(), dst_basename.c_str());
1639 const uid_t caller_uid = ::geteuid();
1640 const ::uid_t source_uid = 0 == caller_uid ? target_stats->
uid() : -1;
1641 if( 0 != ::fchown(dst_dirfd, source_uid, target_stats->
gid()) ) {
1642 if( errno != EPERM && errno != EINVAL ) {
1643 ERR_PRINT(
"dir_preserve ownership of file failed, uid(caller %" PRIu32
", chown %" PRIu32
"), source %s, dest '%s'",
1644 caller_uid, source_uid, src_stats.to_string().c_str(), dst_basename.c_str());
1649 jau::fprintf_td(stderr,
"copy: Ignored: dir_preserve ownership of file failed, uid(caller %" PRIu32
", chown %" PRIu32
"), source %s, dest '%s', errno %d (%s)\n",
1650 caller_uid, source_uid, src_stats.to_string().c_str(), dst_basename.c_str(), errno, ::strerror(errno));
1655 if( 0 != ::fsync(dst_dirfd) ) {
1656 ERR_PRINT(
"Couldn't synchronize destination file '%s'", dst_basename.c_str());
1677 if( source_stats.
is_file() ) {
1681 if( target_stats.
exists() ) {
1682 if( target_stats.
is_file() ) {
1685 jau::fprintf_td(stderr,
"copy: Error: source_path is file, target_path existing file w/o overwrite, source %s, target %s\n",
1693 if ( 0 > src_dirfd ) {
1694 ERR_PRINT(
"source_path dir couldn't be opened, source %s", source_stats.
to_string().c_str());
1698 std::string dst_basename;
1700 if( target_stats.
is_dir() ) {
1703 if ( 0 > dst_dirfd ) {
1704 ERR_PRINT(
"target dir couldn't be opened, target %s", target_stats.
to_string().c_str());
1712 if( !target_parent_stats.
is_dir() ) {
1714 jau::fprintf_td(stderr,
"copy: Error: target parent is not an existing directory, target %s, target_parent %s\n",
1721 if ( 0 > dst_dirfd ) {
1722 ERR_PRINT(
"target_parent dir couldn't be opened, target %s, target_parent %s",
1729 if( !
copy_file(src_dirfd, source_stats, dst_dirfd, dst_basename, copts) ) {
1736 if( !source_stats.
is_dir() ) {
1738 jau::fprintf_td(stderr,
"copy: Error: source_path is neither file nor dir, source %s, target %s\n",
1747 copy_context_t ctx { copts, 0, std::vector<int>(), std::vector<int>() };
1755 if( target_stats.
exists() && !target_stats.
is_dir() ) {
1757 jau::fprintf_td(stderr,
"copy: Error: source_path is dir but target_path exist and is no dir, source %s, target %s\n",
1767 if ( 0 > dst_dirfd ) {
1768 ERR_PRINT(
"target dir couldn't be opened, target %s", target_stats.
to_string().c_str());
1771 ctx.dst_dirfds.push_back(dst_dirfd);
1776 if( !target_parent_stats.
is_dir() ) {
1778 jau::fprintf_td(stderr,
"copy: Error: target parent is not an existing directory, target %s, target_parent %s\n",
1784 if ( 0 > dst_parent_dirfd ) {
1785 ERR_PRINT(
"target dirname couldn't be opened, target %s, target_parent %s",
1789 ctx.dst_dirfds.push_back(dst_parent_dirfd);
1791 if( target_stats.
is_dir() ) {
1794 if ( 0 > dst_dirfd ) {
1795 ERR_PRINT(
"target dir couldn't be opened, target %s", target_stats.
to_string().c_str());
1798 ctx.dst_dirfds.push_back(dst_dirfd);
1804 ctx.skip_dst_dir_mkdir = 1;
1817 ERR_PRINT(
"dirfd stack error: count[src %zu, dst %zu, dst_skip %d] @ %s",
1821 const int src_dirfd = ctx_ptr->
src_dirfds.back();
1822 const int dst_dirfd = ctx_ptr->
dst_dirfds.back();
1823 const std::string& basename_ = element_stats.
item().
basename();
1828 const file_stats target_stats_(dst_dirfd, basename_);
1835 ERR_PRINT(
"dirfd stack error: count[src %zu, dst %zu] @ %s",
1845 if( !
copy_file(src_dirfd, element_stats, dst_dirfd, basename_, ctx_ptr->
copts) ) {
1851 bool res =
jau::fs::visit(source_stats, topts, pv, &ctx.src_dirfds);
1852 while( !ctx.dst_dirfds.empty() ) {
1853 ::close(ctx.dst_dirfds.back());
1854 ctx.dst_dirfds.pop_back();
1863 ERR_PRINT(
"oldpath doesn't exist, oldpath %s, newpath %s\n",
1867 if( 0 !=
::rename(oldpath_stats.
path().c_str(), newpath_stats.
path().c_str()) ) {
1868 ERR_PRINT(
"rename failed, oldpath %s, newpath %s\n",
1880 if( 0 != ::seteuid(user_id) ) {
1881 ERR_PRINT(
"seteuid(%" PRIu32
") failed", user_id);
1888 const mountflags_t flags,
const std::string& fs_options)
1896 if( !target_stats.
is_dir()) {
1900 const std::string target_path(target_stats.
path());
1902 if( 0 > backingfile ) {
1903 ERR_PRINT(
"Couldn't open image-file '%s': res %d", image_stats.
to_string().c_str(), backingfile);
1906#if defined(__linux__)
1907 const ::uid_t caller_uid = ::geteuid();
1908 int loop_device_id = -1;
1910 ::pid_t pid = ::fork();
1912 int loop_ctl_fd = -1, loop_device_fd = -1;
1913 char loopname[4096];
1915 void* fs_options_cstr =
nullptr;
1917 if( 0 != caller_uid ) {
1923 if( 0 > loop_ctl_fd ) {
1924 ERR_PRINT(
"Couldn't open loop-control: res %d", loop_ctl_fd);
1928 loop_device_id = ::ioctl(loop_ctl_fd, LOOP_CTL_GET_FREE);
1929 if( 0 > loop_device_id ) {
1930 ERR_PRINT(
"Couldn't get free loop-device: res %d", loop_device_id);
1933 if( 254 < loop_device_id ) {
1934 ERR_PRINT(
"loop-device %d out of valid range [0..254]", loop_device_id);
1937 ::close(loop_ctl_fd);
1940 snprintf(loopname,
sizeof(loopname),
"/dev/loop%d", loop_device_id);
1944 if( 0 > loop_device_fd ) {
1945 ERR_PRINT(
"Couldn't open loop-device '%s': res %d", loopname, loop_device_fd);
1948 if( 0 > ::ioctl(loop_device_fd, LOOP_SET_FD, backingfile) ) {
1949 ERR_PRINT(
"Couldn't attach image-file '%s' to loop-device '%s'", image_stats.
to_string().c_str(), loopname);
1953 if( fs_options.size() > 0 ) {
1954 fs_options_cstr = (
void*) fs_options.data();
1956 mount_res =
::mount(loopname, target_path.c_str(), fs_type.c_str(), flags, fs_options_cstr);
1957 if( 0 != mount_res ) {
1958 ERR_PRINT(
"source_path %s, target_path %s, fs_type %s, res %d",
1959 image_stats.
path().c_str(), target_path.c_str(), fs_type.c_str(), mount_res);
1960 ::ioctl(loop_device_fd, LOOP_CLR_FD, 0);
1963 ::close(loop_device_fd);
1964 ::_exit(loop_device_id+1);
1967 if( 0 <= loop_ctl_fd ) {
1968 ::close(loop_ctl_fd);
1970 if( 0 <= loop_device_fd ) {
1971 ::close(loop_device_fd);
1974 }
else if( 0 < pid ) {
1976 ::pid_t child_pid = ::waitpid(pid, &pid_status, 0);
1977 if( 0 > child_pid ) {
1978 ERR_PRINT(
"wait(%d) failed: child_pid %d", pid, child_pid);
1980 if( child_pid != pid ) {
1981 WARN_PRINT(
"wait(%d) terminated child_pid %d", pid, child_pid);
1983 if( !WIFEXITED(pid_status) ) {
1984 WARN_PRINT(
"wait(%d) terminated abnormally child_pid %d, pid_status %d", pid, child_pid, pid_status);
1987 loop_device_id = WEXITSTATUS(pid_status);
1988 if( 0 >= loop_device_id ) {
1994 ERR_PRINT(
"Couldn't fork() process: res %d", pid);
1997 if( 0 <= backingfile ) {
1998 ::close(backingfile);
2000 return mount_ctx(target_path, loop_device_id);
2008 if( 0 <= backingfile ) {
2009 ::close(backingfile);
2015 const mountflags_t flags,
const std::string& fs_options)
2017 if( source.empty() ) {
2018 ERR_PRINT(
"source is an empty string ");
2023 if( !target_stats.
is_dir()) {
2027 const std::string target_path(target_stats.
path());
2028 const ::uid_t caller_uid = ::geteuid();
2030 ::pid_t pid = ::fork();
2032 void* fs_options_cstr =
nullptr;
2034 if( 0 != caller_uid ) {
2036 ::_exit( EXIT_FAILURE );
2039 if( fs_options.size() > 0 ) {
2040 fs_options_cstr = (
void*) fs_options.data();
2042#if defined(__linux__)
2043 const int mount_res =
::mount(source_stats.
path().c_str(), target_path.c_str(), fs_type.c_str(), flags, fs_options_cstr);
2044#elif defined(__FreeBSD__)
2051 (void)fs_options_cstr;
2052 const int mount_res = -1;
2054 #if !defined(JAU_OS_TYPE_WASM)
2055 #warning Add OS support
2058 (void)fs_options_cstr;
2059 const int mount_res = -1;
2061 if( 0 != mount_res ) {
2062 ERR_PRINT(
"source_path %s, target_path %s, fs_type %s, flags %" PRIu64
", res %d",
2063 source_stats.
path().c_str(), target_path.c_str(), fs_type.c_str(), flags, mount_res);
2064 ::_exit( EXIT_FAILURE );
2066 ::_exit( EXIT_SUCCESS );
2069 }
else if( 0 < pid ) {
2071 ::pid_t child_pid = ::waitpid(pid, &pid_status, 0);
2072 if( 0 > child_pid ) {
2073 ERR_PRINT(
"wait(%d) failed: child_pid %d", pid, child_pid);
2075 if( child_pid != pid ) {
2076 WARN_PRINT(
"wait(%d) terminated child_pid %d", pid, child_pid);
2078 if( !WIFEXITED(pid_status) ) {
2079 WARN_PRINT(
"wait(%d) terminated abnormally child_pid %d, pid_status %d", pid, child_pid, pid_status);
2080 }
else if( EXIT_SUCCESS == WEXITSTATUS(pid_status) ) {
2085 ERR_PRINT(
"Couldn't fork() process: res %d", pid);
2096 if( !target_stats.
is_dir()) {
2099 const ::uid_t caller_uid = ::geteuid();
2101 ::pid_t pid = ::fork();
2103 if( 0 != caller_uid ) {
2105 ::_exit( EXIT_FAILURE );
2108#if defined(__linux__)
2109 const int umount_res = ::umount2(target_stats.
path().c_str(), flags);
2110#elif defined(__FreeBSD__)
2111 const int umount_res = ::unmount(target_stats.
path().c_str(), flags);
2113 #if !defined(JAU_OS_TYPE_WASM)
2114 #warning Add OS support
2116 const int umount_res = -1;
2118 if( 0 != umount_res ) {
2119 ERR_PRINT(
"Couldn't umount '%s', flags %d: res %d\n", target_stats.
to_string().c_str(), flags, umount_res);
2123 ::_exit(0 == umount_res ? EXIT_SUCCESS : EXIT_FAILURE);
2125#if defined(__linux__)
2126 int loop_device_fd = -1;
2127 char loopname[4096];
2129 snprintf(loopname,
sizeof(loopname),
"/dev/loop%d", context.
loop_device_id);
2133 if( 0 > loop_device_fd ) {
2134 ERR_PRINT(
"Couldn't open loop-device '%s': res %d", loopname, loop_device_fd);
2137 if( 0 > ::ioctl(loop_device_fd, LOOP_CLR_FD, 0) ) {
2138 ERR_PRINT(
"Couldn't detach loop-device '%s'", loopname);
2141 ::close(loop_device_fd);
2142 ::_exit(0 == umount_res ? EXIT_SUCCESS : EXIT_FAILURE);
2146 ::_exit( EXIT_FAILURE );
2148#if defined(__linux__)
2150 if( 0 <= loop_device_fd ) {
2151 ::close(loop_device_fd);
2153 ::_exit( EXIT_FAILURE );
2155 }
else if( 0 < pid ) {
2157 ::pid_t child_pid = ::waitpid(pid, &pid_status, 0);
2158 if( 0 > child_pid ) {
2159 ERR_PRINT(
"wait(%d) failed: child_pid %d", pid, child_pid);
2161 if( child_pid != pid ) {
2162 WARN_PRINT(
"wait(%d) terminated child_pid %d", pid, child_pid);
2164 if( !WIFEXITED(pid_status) ) {
2165 WARN_PRINT(
"wait(%d) terminated abnormally child_pid %d, pid_status %d", pid, child_pid, pid_status);
2166 }
else if( EXIT_SUCCESS == WEXITSTATUS(pid_status) ) {
2171 ERR_PRINT(
"Couldn't fork() process: res %d", pid);
2178 if( target.empty() ) {
2182 if( !target_stats.
is_dir()) {
2185 const ::uid_t caller_uid = ::geteuid();
2187 ::pid_t pid = ::fork();
2189 if( 0 != caller_uid ) {
2191 ::_exit( EXIT_FAILURE );
2194#if defined(__linux__)
2195 const int umount_res = ::umount2(target_stats.
path().c_str(), flags);
2196#elif defined(__FreeBSD__)
2197 const int umount_res = ::unmount(target_stats.
path().c_str(), flags);
2199 #if !defined(JAU_OS_TYPE_WASM)
2200 #warning Add OS support
2202 const int umount_res = -1;
2204 if( 0 == umount_res ) {
2205 ::_exit( EXIT_SUCCESS );
2207 ERR_PRINT(
"Couldn't umount '%s', flags %d: res %d\n", target_stats.
to_string().c_str(), flags, umount_res);
2208 ::_exit( EXIT_FAILURE );
2210 }
else if( 0 < pid ) {
2212 ::pid_t child_pid = ::waitpid(pid, &pid_status, 0);
2213 if( 0 > child_pid ) {
2214 ERR_PRINT(
"wait(%d) failed: child_pid %d", pid, child_pid);
2216 if( child_pid != pid ) {
2217 WARN_PRINT(
"wait(%d) terminated child_pid %d", pid, child_pid);
2219 if( !WIFEXITED(pid_status) ) {
2220 WARN_PRINT(
"wait(%d) terminated abnormally child_pid %d, pid_status %d", pid, child_pid, pid_status);
2221 }
else if( EXIT_SUCCESS == WEXITSTATUS(pid_status) ) {
2226 ERR_PRINT(
"Couldn't fork() process: res %d", pid);
Safe base 38 alphabet with ASCII code-point sorting order.
Representing a directory item split into dirname() and basename().
std::string path() const noexcept
Returns a full unix path representation combining dirname() and basename().
const std::string & basename() const noexcept
Return the basename, shall not be empty nor contain a dirname.
std::string to_string() const noexcept
Returns a comprehensive string representation of this item.
dir_item() noexcept
Empty item w/ .
const std::string & dirname() const noexcept
Returns the dirname, shall not be empty and denotes .
Platform agnostic representation of POSIX ::lstat() and ::stat() for a given pathname.
int fd() const noexcept
Returns the file descriptor if has_fd(), otherwise -1 for no file descriptor.
constexpr bool exists() const noexcept
Returns true if entity does not exist, exclusive bit.
constexpr bool has_fd() const noexcept
Returns true if entity has a file descriptor.
const fraction_timespec & mtime() const noexcept
Returns the last modification time of this element since Unix Epoch.
constexpr bool is_link() const noexcept
Returns true if entity is a symbolic link, might be in combination with is_file(),...
constexpr bool is_file() const noexcept
Returns true if entity is a file, might be in combination with is_link().
const file_stats * final_target(size_t *link_count=nullptr) const noexcept
Returns the final target element, either a pointer to this instance if not a symbolic-link or the fin...
gid_t gid() const noexcept
Returns the group id, owning the element.
constexpr bool is_dir() const noexcept
Returns true if entity is a directory, might be in combination with is_link().
const dir_item & item() const noexcept
Returns the dir_item.
bool has(const field_t fields) const noexcept
Returns true if the given field_t fields were retrieved, otherwise false.
field_t
Field identifier which bit-mask indicates field_t fields.
@ mode
POSIX file protection mode bits.
@ type
File type mode bits.
constexpr bool has_access() const noexcept
Returns true if entity gives no access to user, exclusive bit.
file_stats() noexcept
Instantiate an empty file_stats with fmode_t::not_existing set.
const std::shared_ptr< file_stats > & link_target() const noexcept
Returns the link-target this symbolic-link points to if instance is a symbolic-link,...
std::string to_string() const noexcept
Returns a comprehensive string representation of this element.
uid_t uid() const noexcept
Returns the user id, owning the element.
bool operator==(const file_stats &rhs) const noexcept
fmode_t prot_mode() const noexcept
Returns the POSIX protection bit portion of fmode_t, i.e.
const fraction_timespec & atime() const noexcept
Returns the last access time of this element since Unix Epoch.
constexpr field_t fields() const noexcept
Returns the retrieved field_t fields.
std::string path() const noexcept
Returns the unix path representation.
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
#define ABORT(...)
Use for unconditional ::abort() call with given messages, prefix '[elapsed_time] ABORT @ file:line fu...
#define WARN_PRINT(...)
Use for unconditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE FUNC: '.
static bool set_effective_uid(::uid_t user_id)
static bool copy_file(const int src_dirfd, const file_stats &src_stats, const int dst_dirfd, const std::string &dst_basename, const copy_options copts) noexcept
static const std::string s_dot_slash("./")
static const std::string s_slash("/")
static bool copy_dir_preserve(const file_stats &src_stats, const int dst_dirfd, const std::string &dst_basename, const copy_options copts) noexcept
static const char c_backslash('\\')
static const char c_slash('/')
static const std::string s_slash_dotdot_slash("/../")
static const std::string s_slash_dot("/.")
static void _append_bitstr(std::string &out, fmode_t mask, fmode_t bit, const std::string &bitstr)
static const std::string s_slash_dot_slash("/./")
static const std::string s_dotdot("..")
struct ::stat64 struct_stat64
#define __posix_fstatat64
constexpr const int _open_dir_flags
static const std::string s_slash_dotdot("/..")
static bool _visit(const file_stats &item_stats, const traverse_options topts, const path_visitor &visitor, std::vector< int > &dirfds) noexcept
static bool _dir_item_basename_compare(const dir_item &a, const dir_item &b)
static const std::string s_dot(".")
static bool copy_push_mkdir(const file_stats &dst_stats, copy_context_t &ctx) noexcept
std::string to_string(const endian_t v) noexcept
Return std::string representation of the given endian.
std::string encode(int num, const alphabet &aspec, const unsigned int min_width=0) noexcept
Encodes a given positive decimal number to a symbolic string representing a given alphabet and its ba...
constexpr bool has_any(const E mask, const E bits) noexcept
constexpr bool is_set(const E mask, const E bits) noexcept
constexpr E & write(E &store, const E bits, bool set) noexcept
If set==true, sets the bits in store, i.e.
bool exists(const std::string &path, bool verbose_on_error=false) noexcept
Returns true if path exists and is accessible.
bool mkdir(const std::string &path, const fmode_t mode=jau::fs::fmode_t::def_dir_prot, const bool verbose=false) noexcept
Create directory.
uint64_t mountflags_t
Generic flag bit values for mount() flags.
std::string lookup_asset_dir(const char *exe_path, const char *asset_file, const char *asset_install_subdir) noexcept
Returns located asset directory if found, otherwise an empty string.
std::string to_named_fd(const int fd) noexcept
Returns platform dependent named file descriptor of given file descriptor, if supported.
mount_ctx mount(const std::string &source, const std::string &target, const std::string &fs_type, const mountflags_t flags, const std::string &fs_options="")
Attach the filesystem named in source to target using the given filesystem source directly.
mount_ctx mount_image(const std::string &image_path, const std::string &target, const std::string &fs_type, const mountflags_t flags, const std::string &fs_options="")
Attach the filesystem image named in image_path to target using an intermediate platform specific fil...
std::string to_string(const fmode_t mask, const bool show_rwx) noexcept
Return the string representation of fmode_t.
bool copy(const std::string &source_path, const std::string &dest_path, const copy_options copts=copy_options::none) noexcept
Copy the given source_path to dest_path using copy_options.
fmode_t
Generic file type and POSIX protection mode bits as used in file_stats, touch(), mkdir() etc.
std::string basename(const std::string_view &path) noexcept
Return stripped leading directory components from given path separated by /.
std::string get_cwd() noexcept
Return the current working directory or empty on failure.
bool compare(const file_stats &source1, const file_stats &source2, const bool verbose=false) noexcept
Compare the bytes of both files, denoted by source1 and source2.
bool visit(const std::string &path, const traverse_options topts, const path_visitor &visitor, std::vector< int > *dirfds=nullptr) noexcept
Visit element(s) of a given path, see traverse_options for detailed settings.
std::string absolute(const std::string_view &relpath) noexcept
Returns the absolute path of given relpath if existing, otherwise an empty string.
int umountflags_t
Generic flag bit values for umount() flags.
std::string dirname(const std::string_view &path) noexcept
Return stripped last component from given path separated by /, excluding the trailing separator /.
copy_options
Filesystem copy options used to copy() path elements.
bool get_dir_content(const std::string &path, const consume_dir_item &digest) noexcept
Returns a list of directory elements excluding .
bool touch(const std::string &path, const jau::fraction_timespec &atime, const jau::fraction_timespec &mtime, const fmode_t mode=jau::fs::fmode_t::def_file_prot) noexcept
Touch the file with given atime and mtime and create file if not existing yet.
traverse_options
Filesystem traverse options used to visit() path elements.
bool rename(const std::string &oldpath, const std::string &newpath) noexcept
Rename oldpath to newpath using POSIX rename(), with the following combinations.
jau::function< void(const dir_item &)> consume_dir_item
void consume_dir_item(const dir_item& item)
bool remove(const std::string &path, const traverse_options topts=traverse_options::none) noexcept
Remove the given path.
void sync() noexcept
Synchronizes filesystems, i.e.
int from_named_fd(const std::string &named_fd) noexcept
Returns the file descriptor from the given named file descriptor.
bool umount(const mount_ctx &context, const umountflags_t flags)
Detach the given mount_ctx context
bool chdir(const std::string &path) noexcept
Change working directory.
bool isAbsolute(const std::string_view &path) noexcept
Returns true if first character is / or - in case of Windows - \\.
traverse_event
Filesystem traverse event used to call path_visitor for path elements from visit().
constexpr ::mode_t posix_protection_bits(const fmode_t mask) noexcept
Returns the POSIX protection bits: rwx_all | set_uid | set_gid | sticky, i.e.
jau::function< bool(traverse_event, const file_stats &, size_t)> path_visitor
path_visitor jau::FunctionDef definition
@ exec_usr
Protection bit: POSIX S_IXUSR.
@ exec_grp
Protection bit: POSIX S_IXGRP.
@ write_grp
Protection bit: POSIX S_IWGRP.
@ no_access
Type: Entity gives no access to user, exclusive bit.
@ set_uid
Protection bit: POSIX S_ISUID.
@ write_usr
Protection bit: POSIX S_IWUSR.
@ ugs_set
Protection bit: POSIX S_ISUID | S_ISGID | S_ISVTX.
@ link
Type: Entity is a symbolic link, might be in combination with file or dir, fifo, chr,...
@ sock
Type: Entity is a socket, might be in combination with link.
@ chr
Type: Entity is a character device, might be in combination with link.
@ rwx_oth
Protection bit: POSIX S_IRWXO.
@ sticky
Protection bit: POSIX S_ISVTX.
@ rwx_usr
Protection bit: POSIX S_IRWXU.
@ read_usr
Protection bit: POSIX S_IRUSR.
@ dir
Type: Entity is a directory, might be in combination with link.
@ file
Type: Entity is a file, might be in combination with link.
@ protection_mask
12 bit protection bit mask 07777 for rwx_all | set_uid | set_gid | sticky .
@ blk
Type: Entity is a block device, might be in combination with link.
@ read_grp
Protection bit: POSIX S_IRGRP.
@ read_oth
Protection bit: POSIX S_IROTH.
@ not_existing
Type: Entity does not exist, exclusive bit.
@ rwx_grp
Protection bit: POSIX S_IRWXG.
@ fifo
Type: Entity is a fifo/pipe, might be in combination with link.
@ write_oth
Protection bit: POSIX S_IWOTH.
@ set_gid
Protection bit: POSIX S_ISGID.
@ exec_oth
Protection bit: POSIX S_IXOTH.
@ ignore_symlink_errors
Ignore errors from erroneous symlinks, e.g.
@ verbose
Enable verbosity mode, show error messages on stderr.
@ follow_symlinks
Copy referenced symbolic linked files or directories instead of just the symbolic link with property ...
@ sync
Ensure data and meta-data file synchronization is performed via ::fsync() after asynchronous copy ope...
@ overwrite
Overwrite existing destination files.
@ preserve_all
Preserve uid and gid if allowed and access- and modification-timestamps, i.e.
@ into_existing_dir
Copy source dir content into an already existing destination directory as if destination directory di...
@ recursive
Traverse through directories, i.e.
@ lexicographical_order
Traverse through elements in lexicographical order.
@ verbose
Enable verbosity mode, potentially used by a path_visitor implementation like remove().
@ follow_symlinks
Traverse through symbolic linked directories if traverse_options::recursive is set,...
@ dir_check_entry
Call path_visitor at directory entry, allowing path_visitor to skip traversal of this directory if re...
@ dir_entry
Call path_visitor at directory entry.
@ dir_exit
Call path_visitor at directory exit.
@ recursive
Traverse through directories, i.e.
@ dir_symlink
Visiting a symbolic-link to a directory which is not followed, i.e.
@ none
No value, neither file, symlink nor dir_entry or dir_exit.
@ dir_non_recursive
Visiting a directory non-recursive, i.e.
@ file
Visiting a file, may be in conjunction with symlink, i.e.
@ dir_check_entry
Visiting a directory on entry, see traverse_options::dir_check_entry.
@ dir_entry
Visiting a directory on entry, see traverse_options::dir_entry.
@ symlink
Visiting a symbolic-link, either to a file or a non-existing entity.
@ dir_exit
Visiting a directory on exit, see traverse_options::dir_exit.
jau::function< R(A...)> bind_capref(I *data_ptr, R(*func)(I *, A...)) noexcept
Bind given data by passing the captured reference (pointer) to the value and non-void function to an ...
constexpr bool is_windows() noexcept
Evaluates true if platform os_type::native contains os_type::Windows.
void zero_bytes_sec(void *s, size_t n) noexcept __attrdecl_no_optimize__
Wrapper to ::explicit_bzero(), ::bzero() or ::memset(), whichever is available in that order.
std::string to_decstring(const value_type &v, const char separator=',', const nsize_t width=0) noexcept
Produce a decimal string representation of an integral integer value.
Author: Sven Gothel sgothel@jausoft.com Copyright (c) 2022 Gothel Software e.K.
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
void INFO_PRINT(const char *format,...) noexcept
Use for unconditional informal messages, prefix '[elapsed_time] Info: '.
int fprintf_td(const uint64_t elapsed_ms, FILE *stream, const char *format,...) noexcept
Convenient fprintf() invocation, prepending the given elapsed_ms timestamp.
std::vector< int > dst_dirfds
std::vector< int > src_dirfds
Timespec structure using int64_t for its components in analogy to struct timespec_t on 64-bit platfor...
constexpr struct timespec to_timespec() const noexcept
Return conversion to POSIX struct timespec, potentially narrowing the components if underlying system...