46 #include <sys/types.h>
48 #include <sys/mount.h>
49 #if defined(__linux__)
50 #include <sys/sendfile.h>
51 #include <linux/loop.h>
67#if defined(__linux__) && defined(__GLIBC__)
69 #define _USE_SENDFILE_ 1
70#elif defined(__linux__)
71 #define _USE_SENDFILE_ 1
74#if defined(__FreeBSD__)
76 typedef off_t off64_t;
77 #define __posix_fstatat64 ::fstatat
78 #define __posix_openat64 ::openat
81 #define __posix_fstatat64 ::fstatat64
82 #define __posix_openat64 ::openat64
86 const size_t bsz = PATH_MAX;
91 char* res = ::getcwd(&str[0], bsz);
92 if( res == &str[0] ) {
93 str.resize(::strnlen(res, bsz));
102 return 0 ==
::chdir(path.c_str());
106 const size_t bsz = PATH_MAX;
111 char *res = ::realpath(&relpath[0], &str[0]);
112 if( res == &str[0] ) {
113 str.resize(::strnlen(res, bsz));
117 return std::string();
133 if( 0 == path.size() ) {
137 if(
c_slash == path[path.size()-1] ) {
138 if( 1 == path.size() ) {
139 return std::string(path);
141 end_pos = path.size()-2;
143 end_pos = path.size()-1;
145 size_t idx = path.find_last_of(
c_slash, end_pos);
146 if( idx == std::string_view::npos ) {
150 return std::string( path.substr(0, std::max<size_t>(1, idx)) );
155 if( 0 == path.size() ) {
159 if(
c_slash == path[path.size()-1] ) {
160 if( 1 == path.size() ) {
161 return std::string(path);
163 end_pos = path.size()-2;
165 end_pos = path.size()-1;
167 size_t idx = path.find_last_of(
c_slash, end_pos);
168 if( idx == std::string_view::npos ) {
169 return std::string( path.substr(0, end_pos+1));
171 return std::string( path.substr(idx+1, end_pos-idx) );
177 if( res.length() < suffix.length() ) {
180 size_t n = res.length() - suffix.length();
181 size_t idx = res.find(suffix, n);
182 if( idx == std::string_view::npos ) {
185 return res.substr(0, n);
189std::string
jau::io::fs::basename(
const std::string_view& path,
const std::vector<std::string_view>& suffixes)
noexcept {
191 for(std::string_view suffix : suffixes) {
192 if( res.length() >= suffix.length() ) {
193 size_t n = res.length() - suffix.length();
194 size_t idx = res.find(suffix, n);
195 if( idx != std::string_view::npos ) {
196 return res.substr(0, n);
204 return path.size() > 0 &&
210 ::memset(&s, 0,
sizeof(s));
213 if( 0 != stat_res && verbose_on_error ) {
214 fprintf(stderr,
"exists '%s': %d: %d %s\n", path.c_str(), stat_res, errno, strerror(errno));
216 return 0 == stat_res;
224 std::string assetdir0 =
"resources";
225 if(
exists( assetdir0+
"/"+asset_file ) ) {
228 if( !exe_path || !asset_install_subdir ) {
236 if(
exists( assetdir1+
"/"+asset_file ) ) {
239 fprintf(stderr,
"asset_dir: Not found: dir '%s', file '%s', exe[path '%s', dir '%s'], cwd '%s', adir '%s'\n",
240 assetdir1.c_str(), asset_file, exe_path, exedir.c_str(), cwd.c_str(), adir.c_str());
244std::unique_ptr<dir_item::backed_string_view> dir_item::reduce(
const std::string_view& path_)
noexcept {
245 constexpr const bool _debug =
false;
247 if constexpr ( _debug ) {
248 jau::fprintf_td(stderr,
"X.0: path '%s'\n", std::string(path_).c_str());
251 std::unique_ptr<dir_item::backed_string_view> path2 = std::make_unique<dir_item::backed_string_view>(path_);
259 path2->view = path2->view.substr(2, path2->view.size()-2);
263 if(
c_slash == path2->view[path2->view.size()-1] &&
264 ( path2->view.size() < 3 || std::string_view::npos == path2->view.find(
s_slash_dot_slash, path2->view.size()-3) ) &&
265 ( path2->view.size() < 4 || std::string_view::npos == path2->view.find(
s_slash_dotdot_slash, path2->view.size()-4) )
268 path2->view = path2->view.substr(0, path2->view.size()-1);
272 if( ( path2->view.size() >= 3 && std::string_view::npos != path2->view.find(
s_slash_dotdot, path2->view.size()-3) ) ||
273 ( path2->view.size() >= 2 && std::string_view::npos != path2->view.find(
s_slash_dot, path2->view.size()-2) ) ) {
275 path2->view = path2->backing.append(
s_slash);
278 if constexpr ( _debug ) {
279 jau::fprintf_td(stderr,
"X.1: path2 '%s'\n", path2->to_string(
true).c_str());
287 if constexpr ( _debug ) {
288 jau::fprintf_td(stderr,
"X.2.1: path2: spos %zu, idx %zu, '%s'\n", spos, idx, path2->to_string(
true).c_str());
290 if( std::string_view::npos == idx ) {
293 std::string_view pre = path2->view.substr(0, idx);
294 if( 0 == pre.size() ) {
296 path2->view = path2->view.substr(idx+2);
300 const std::string post( path2->view.substr(idx+2) );
301 path2->backup_and_append( pre, post );
304 if constexpr ( _debug ) {
305 jau::fprintf_td(stderr,
"X.2.2: path2: spos %zu, '%s'\n", spos, path2->to_string(
true).c_str());
307 }
while( spos <= path2->view.size()-3 );
308 if constexpr ( _debug ) {
309 jau::fprintf_td(stderr,
"X.2.X: path2: '%s'\n", path2->to_string(
true).c_str());
316 if constexpr ( _debug ) {
317 jau::fprintf_td(stderr,
"X.3.1: path2: spos %zu, idx %zu, '%s'\n", spos, idx, path2->to_string(
true).c_str());
319 if( std::string_view::npos == idx ) {
324 WARN_PRINT(
"dir_item::resolve: '..' resolution error: '%s' -> '%s'", std::string(path_).c_str(), path2->to_string().c_str());
327 std::string_view pre = path2->view.substr(0, idx);
331 }
else if( 3 <= idx &&
s_slash_dotdot == path2->view.substr(idx-3, 3) ) {
338 path2->view = path2->view.substr(idx+3);
340 }
else if(
s_dot == pre_str ) {
342 path2->view = path2->view.substr(idx+4);
346 const std::string post( path2->view.substr(idx+3) );
347 path2->backup_and_append( pre_str, post );
348 spos = pre_str.size();
351 if constexpr ( _debug ) {
352 jau::fprintf_td(stderr,
"X.3.2: path2: spos %zu, '%s'\n", spos, path2->to_string(
true).c_str());
354 }
while( spos <= path2->view.size()-4 );
355 if constexpr ( _debug ) {
356 jau::fprintf_td(stderr,
"X.3.X: path2: '%s'\n", path2->to_string(
true).c_str());
360 if(
c_slash == path2->view[path2->view.size()-1] ) {
361 path2->view = path2->view.substr(0, path2->view.size()-1);
363 if constexpr ( _debug ) {
364 jau::fprintf_td(stderr,
"X.X: path2: '%s'\n", path2->to_string(
true).c_str());
377: dirname_(std::move(dirname__)), basename_(std::move(basename__)), empty_(dirname_.empty() && basename_.empty()) {
381: dirname_(
s_dot), basename_(
s_dot), empty_(true) {}
384: dir_item( reduce(path_) )
389 if(
s_dot == dirname_ ) {
392 if(
s_dot == basename_ ) {
396 return dirname_ + basename_;
398 return dirname_ +
s_slash + basename_;
407 if(
is_set( mask, bit )) {
424 const std::string r(
"r");
425 const std::string w(
"w");
426 const std::string x(
"x");
439 out.append(std::string(buf, len));
449 std::string res(
"/dev/fd/");
450 res.append(std::to_string(fd));
456 if( 1 == sscanf(named_fd.c_str(),
"/dev/fd/%d", &scan_value) ) {
459 }
else if( 1 == sscanf(named_fd.c_str(),
"/proc/self/fd/%d", &scan_value) ) {
468 uid_(0), gid_(0), size_(0), btime_(), atime_(), ctime_(), mtime_(),
473 static constexpr bool jau_has_stat(
const uint32_t mask,
const uint32_t bit) {
return bit == ( mask & bit ); }
478 uid_(0), gid_(0), size_(0), btime_(), atime_(), ctime_(), mtime_(), errno_res_(0)
480 constexpr const bool _debug =
false;
482 const std::string full_path(
item.empty() ?
"" :
item.path() );
483 if(
item.empty() && AT_FDCWD != dirfd ) {
489 ERR_PRINT(
"rec_level %d, dirfd %d < 0, %s, dirfd_is_item_dirname %d, AT_EMPTY_PATH",
490 (
int)cc.rec_level, dirfd,
item.toString().c_str(), dirfd_is_item_dirname);
496 if( 0 <= scan_value ) {
500 }
else if( full_path.starts_with(
"/dev/fd/pipe:") ) {
508 if constexpr ( _debug ) {
509 jau::fprintf_td(stderr,
"file_stats(%d): FIFO: '%s', errno %d (%s)\n", (
int)cc.rec_level,
toString().c_str(), errno, ::strerror(errno));
514 const std::string dirfd_path =
has(
field_t::fd ) ?
"" : ( dirfd_is_item_dirname ? item_.basename() : full_path );
519 int stat_res = ::statx(dirfd, dirfd_path.c_str(),
520 ( AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | (
has(
field_t::fd ) ? AT_EMPTY_PATH : 0 ) ),
521 ( STATX_BASIC_STATS | STATX_BTIME ), &s);
522 if( 0 != stat_res ) {
523 if constexpr ( _debug ) {
524 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));
540 if( jau_has_stat( s.stx_mask, STATX_TYPE ) ) {
544 if( S_ISLNK( s.stx_mode ) ) {
547 if( S_ISREG( s.stx_mode ) ) {
549 }
else if( S_ISDIR( s.stx_mode ) ) {
551 }
else if( S_ISFIFO( s.stx_mode ) ) {
553 }
else if( S_ISCHR( s.stx_mode ) ) {
555 }
else if( S_ISSOCK( s.stx_mode ) ) {
557 }
else if( S_ISBLK( s.stx_mode ) ) {
561 if( jau_has_stat( s.stx_mask, STATX_MODE ) ) {
564 mode_ |=
static_cast<fmode_t>( s.stx_mode & ( S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX ) );
566 if( jau_has_stat( s.stx_mask, STATX_NLINK ) ) {
569 if( jau_has_stat( s.stx_mask, STATX_UID ) ) {
573 if( jau_has_stat( s.stx_mask, STATX_GID ) ) {
577 if( jau_has_stat( s.stx_mask, STATX_ATIME ) || 0 != s.stx_atime.tv_sec || 0 != s.stx_atime.tv_nsec ) {
581 if( jau_has_stat( s.stx_mask, STATX_MTIME ) ) {
585 if( jau_has_stat( s.stx_mask, STATX_CTIME ) ) {
589 if( jau_has_stat( s.stx_mask, STATX_INO ) ) {
592 if( jau_has_stat( s.stx_mask, STATX_SIZE ) ) {
598 if( jau_has_stat( s.stx_mask, STATX_BLOCKS ) ) {
601 if( jau_has_stat( s.stx_mask, STATX_BTIME ) ) {
607 std::string link_path;
609 const size_t path_link_max_len = 0 < s.stx_size ? s.stx_size + 1 : PATH_MAX;
610 std::vector<char> buffer;
611 buffer.reserve(path_link_max_len);
612 buffer.resize(path_link_max_len);
613 const ssize_t path_link_len = ::readlinkat(dirfd, dirfd_path.c_str(), buffer.data(), path_link_max_len);
614 if( 0 > path_link_len ) {
616 link_target_ = std::make_shared<file_stats>();
620 link_path = std::string(buffer.data(), path_link_len);
622 link_target_path_ = std::make_shared<std::string>(link_path);
623 if( 0 == cc.rec_level ) {
626 stat_res = ::statx(dirfd, dirfd_path.c_str(), AT_NO_AUTOMOUNT | (
has(
field_t::fd ) ? AT_EMPTY_PATH : 0 ), STATX_BASIC_STATS, &s);
627 if( 0 != stat_res ) {
628 if constexpr ( _debug ) {
629 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));
649 if( 0 < link_path.size() &&
c_slash == link_path[0] ) {
650 link_target_ = std::make_shared<file_stats>(ctor_cookie(cc.rec_level+1), dirfd,
dir_item( link_path ),
false );
652 link_target_ = std::make_shared<file_stats>(ctor_cookie(cc.rec_level+1), dirfd,
dir_item(
jau::io::fs::dirname(full_path), link_path ), dirfd_is_item_dirname );
654 if( link_target_->has_fd() ) {
656 fd_ = link_target_->fd();
658 if( link_target_->is_socket() ) {
660 }
else if( link_target_->is_block() ) {
662 }
else if( link_target_->is_char() ) {
664 }
else if( link_target_->is_fifo() ) {
666 }
else if( link_target_->is_dir() ) {
668 }
else if( link_target_->is_file() ) {
672 size_ = link_target_->size();
674 }
else if( !link_target_->exists() ) {
676 }
else if( !link_target_->has_access() ) {
680 if constexpr ( _debug ) {
681 jau::fprintf_td(stderr,
"file_stats(%d): '%s', %d, errno %d (%s)\n", (
int)cc.rec_level,
toString().c_str(), stat_res, errno, ::strerror(errno));
688 if( 0 != stat_res ) {
689 if constexpr ( _debug ) {
690 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));
709 if( S_ISLNK( s.st_mode ) ) {
712 if( S_ISREG( s.st_mode ) ) {
718 }
else if( S_ISDIR( s.st_mode ) ) {
720 }
else if( S_ISFIFO( s.st_mode ) ) {
722 }
else if( S_ISCHR( s.st_mode ) ) {
724 }
else if( S_ISSOCK( s.st_mode ) ) {
726 }
else if( S_ISBLK( s.st_mode ) ) {
731 mode_ |=
static_cast<fmode_t>( s.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX ) );
741 std::string link_path;
743 const size_t path_link_max_len = 0 < s.st_size ? s.st_size + 1 : PATH_MAX;
744 std::vector<char> buffer;
745 buffer.reserve(path_link_max_len);
746 buffer.resize(path_link_max_len);
747 const ssize_t path_link_len = ::readlinkat(dirfd, dirfd_path.c_str(), buffer.data(), path_link_max_len);
748 if( 0 > path_link_len ) {
750 link_target_ = std::make_shared<file_stats>();
754 link_path = std::string(buffer.data(), path_link_len);
756 link_target_path_ = std::make_shared<std::string>(link_path);
757 if( 0 == cc.rec_level ) {
761 if( 0 != stat_res ) {
762 if constexpr ( _debug ) {
763 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));
783 if( 0 < link_path.size() &&
c_slash == link_path[0] ) {
784 link_target_ = std::make_shared<file_stats>(ctor_cookie(cc.rec_level+1), dirfd,
dir_item( link_path ),
false );
786 link_target_ = std::make_shared<file_stats>(ctor_cookie(cc.rec_level+1), dirfd,
dir_item(
jau::io::fs::dirname(full_path), link_path ), dirfd_is_item_dirname );
788 if( link_target_->has_fd() ) {
790 fd_ = link_target_->fd();
792 if( link_target_->is_socket() ) {
794 }
else if( link_target_->is_block() ) {
796 }
else if( link_target_->is_char() ) {
798 }
else if( link_target_->is_fifo() ) {
800 }
else if( link_target_->is_dir() ) {
802 }
else if( link_target_->is_file() ) {
805 size_ = link_target_->size();
806 }
else if( !link_target_->exists() ) {
808 }
else if( !link_target_->has_access() ) {
812 if constexpr ( _debug ) {
813 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));
845 while(
nullptr != fs1 ) {
850 if(
nullptr != link_count ) {
864 return item_ == rhs.item_ &&
865 has_fields_ == rhs.has_fields_ &&
866 mode_ == rhs.mode_ &&
867 uid_ == rhs.uid_ && gid_ == rhs.gid_ &&
868 errno_res_ == rhs.errno_res_ &&
869 size_ == rhs.size_ &&
870 btime_ == rhs.btime_ &&
871 atime_ == rhs.atime_ &&
872 ctime_ == rhs.ctime_ &&
873 mtime_ == rhs.mtime_ &&
875 ( link_target_path_ == rhs.link_target_path_&&
876 link_target_ == rhs.link_target_
882 std::string stored_path, link_detail;
884 if(
nullptr != link_target_path_ ) {
885 stored_path =
" [-> "+*link_target_path_+
"]";
889 if( 0 < link_count ) {
890 link_detail =
" -(" + std::to_string(link_count) +
")-> '" + final_target_->
path() +
"'";
893 std::string res(
"file_stats[");
895 .append(
", '"+item_.path()+
"'"+stored_path+link_detail );
896 if( 0 == errno_res_ ) {
898 res.append(
", fd " ).append( std::to_string(fd_) );
901 res.append(
", uid " ).append( std::to_string(uid_) );
904 res.append(
", gid " ).append( std::to_string(gid_) );
909 res.append(
", size n/a" );
912 res.append(
", btime " ).append( btime_.toISO8601String() );
915 res.append(
", atime " ).append( atime_.toISO8601String() );
918 res.append(
", ctime " ).append( ctime_.toISO8601String() );
921 res.append(
", mtime " ).append( mtime_.toISO8601String() );
925 res.append(
", errno " ).append( std::to_string(errno_res_) ).append(
", " ).append( std::string(::strerror(errno_res_)) );
939 }
else if( !stats.
exists() ) {
941 if ( 0 != dir_err ) {
956 ERR_PRINT(
"Couldn't open/create file '%s'", path.c_str());
959 struct timespec ts2[2] = { atime.to_timespec(), mtime.to_timespec() };
961 if( 0 != ::futimens(fd, ts2) ) {
962 ERR_PRINT(
"Couldn't update time of file '%s'", path.c_str());
974 ERR_PRINT(
"Couldn't open/create file '%s'", path.c_str());
978 if( 0 != ::futimens(fd,
nullptr ) ) {
979 ERR_PRINT(
"Couldn't update time of file '%s'", path.c_str());
992 if( (
dir = ::opendir( path.c_str() ) ) !=
nullptr ) {
993 while ( ( ent = ::readdir(
dir ) ) !=
nullptr ) {
994 std::string fname( ent->d_name );
1009 int dirfd2 = ::dup(dirfd);
1011 ERR_PRINT(
"Couldn't duplicate given dirfd %d for path '%s'", dirfd, path.c_str());
1014 if( (
dir = ::fdopendir( dirfd2 ) ) !=
nullptr ) {
1015 while ( ( ent = ::readdir(
dir ) ) !=
nullptr ) {
1016 std::string fname( ent->d_name );
1033 const size_t depth = dirfds.size();
1034 if( item_stats.is_dir() ) {
1041 if( dirfds.size() < 1 ) {
1042 ERR_PRINT(
"dirfd stack error: count %zu] @ %s", dirfds.size(), item_stats.toString().c_str());
1045 const int parent_dirfd = dirfds.back();
1047 if ( 0 > this_dirfd ) {
1048 ERR_PRINT(
"entered path dir couldn't be opened, source %s", item_stats.toString().c_str());
1051 dirfds.push_back(this_dirfd);
1055 ::close(this_dirfd);
1062 ::close(this_dirfd);
1067 std::vector<dir_item> content;
1069 ( void(*)(std::vector<dir_item>*,
const dir_item&) )
1070 ( [](std::vector<dir_item>* receiver,
const dir_item& item) ->
void { receiver->push_back( item ); } )
1072 if(
get_dir_content(this_dirfd, item_stats.path(), cs) && content.size() > 0 ) {
1076 for (
const dir_item& element : content) {
1077 const file_stats element_stats( this_dirfd, element,
true );
1078 if( element_stats.
is_dir() ) {
1081 ::close(this_dirfd);
1085 }
else if( !
_visit(element_stats, topts, visitor, dirfds) ) {
1086 ::close(this_dirfd);
1092 element_stats, depth ) )
1094 ::close(this_dirfd);
1100 if( dirfds.size() < 2 ) {
1101 ERR_PRINT(
"dirfd stack error: count %zu] @ %s", dirfds.size(), item_stats.toString().c_str());
1108 ::close(this_dirfd);
1112 else if( item_stats.is_file() || !item_stats.ok() ) {
1121 const bool user_dirfds =
nullptr != dirfds;
1122 if( !user_dirfds ) {
1124 dirfds =
new std::vector<int>();
1125 }
catch (
const std::bad_alloc &e) {
1126 ABORT(
"Error: bad_alloc: dirfds allocation failed");
1130 if( 0 != dirfds->size() ) {
1131 ERR_PRINT(
"dirfd stack error: count %zu @ %s", dirfds->size(), item_stats.toString().c_str());
1137 ERR_PRINT(
"path dirname couldn't be opened, source %s", item_stats.toString().c_str());
1140 dirfds->push_back(dirfd);
1142 bool res =
_visit(item_stats, topts, visitor, *dirfds);
1144 if( dirfds->size() != 1 && res ) {
1145 ERR_PRINT(
"dirfd stack error: count %zu", dirfds->size());
1148 while( !dirfds->empty() ) {
1149 ::close(dirfds->back());
1152 if( !user_dirfds ) {
1167 if( !path_stats.
exists() ) {
1173 if( path_stats.
has_fd() ) {
1183 int res = ::unlink( path_stats.
path().c_str() );
1193 if( !path_stats.
is_dir() ) {
1194 ERR_PRINT(
"remove: Error: path is neither file nor dir: %s\n", path_stats.
toString().c_str());
1204 struct remove_context_t {
1206 std::vector<int> dirfds;
1222 const int dirfd = ctx_ptr->dirfds.back();
1223 const std::string& basename_ = element_stats.
item().
basename();
1227 const int dirfd2 = *( ctx_ptr->dirfds.end() - 2 );
1228 const int res = ::unlinkat( dirfd2, basename_.c_str(), AT_REMOVEDIR );
1237 const int res = ::unlinkat( dirfd, basename_.c_str(), 0 );
1258 if( !source1.is_file() ) {
1259 ERR_PRINT(
"source1_stats is not a file: %s", source1.toString().c_str());
1262 if( !source2.is_file() ) {
1263 ERR_PRINT(
"source2_stats is not a file: %s", source2.toString().c_str());
1267 if( source1.size() != source2.size() ) {
1269 jau::fprintf_td(stderr,
"compare: Source files size mismatch, %s != %s\n",
1270 source1.toString().c_str(), source2.toString().c_str());
1275 int src1=-1, src2=-1;
1276 int src_flags = O_RDONLY|
O_BINARY|O_NOCTTY;
1277 uint64_t offset = 0;
1282 ERR_PRINT(
"Failed to open source1 %s, errno %d, %s", source1.toString().c_str());
1287 ERR_PRINT(
"Failed to open source2 %s, errno %d, %s", source2.toString().c_str());
1290 while ( offset < source1.size()) {
1292 char buffer1[BUFSIZ];
1293 char buffer2[BUFSIZ];
1295 if( ( rc1 =
::read(src1, buffer1,
sizeof(buffer1)) ) > 0 ) {
1296 ssize_t bytes_to_write = rc1;
1297 size_t buffer_offset = 0;
1298 while( 0 <= rc2 && 0 < bytes_to_write ) {
1299 while( ( rc2 =
::read(src2, buffer2+buffer_offset, bytes_to_write) ) < 0 ) {
1300 if ( errno == EAGAIN || errno == EINTR ) {
1306 buffer_offset += rc2;
1307 bytes_to_write -= rc2;
1308 offset += (uint64_t)rc2;
1310 }
else if ( 0 > rc1 && ( errno == EAGAIN || errno == EINTR ) ) {
1314 if ( 0 > rc1 || 0 > rc2 ) {
1316 ERR_PRINT(
"Failed to read source1 bytes @ %s / %s, %s",
1318 source1.toString().c_str());
1319 }
else if ( 0 > rc2 ) {
1320 ERR_PRINT(
"Failed to read source2 bytes @ %s / %s, %s",
1322 source2.toString().c_str());
1326 if( 0 != ::memcmp(buffer1, buffer2, rc1) ) {
1328 jau::fprintf_td(stderr,
"compare: Difference within %s bytes @ %s / %s, %s != %s\n",
1330 source1.toString().c_str(), source2.toString().c_str());
1338 if( offset < source1.size() ) {
1339 ERR_PRINT(
"Incomplete transfer %s / %s, %s != %s\n",
1341 source1.toString().c_str(), source2.toString().c_str());
1363 const int dst_dirfd,
const std::string& dst_basename,
const copy_options copts)
noexcept {
1364 file_stats dst_stats(dst_dirfd, dst_basename);
1370 jau::fprintf_td(stderr,
"copy: Error: dest_path exists but copy_options::overwrite not set: source %s, dest '%s', copts %s\n",
1371 src_stats.toString().c_str(), dst_stats.
toString().c_str(),
to_string( copts ).c_str());
1375 const int res = ::unlinkat(dst_dirfd, dst_basename.c_str(), 0);
1377 ERR_PRINT(
"remove existing dest_path for symbolic-link failed: source %s, dest '%s'",
1378 src_stats.toString().c_str(), dst_stats.
toString().c_str());
1385 const std::shared_ptr<std::string>& link_target_path = src_stats.link_target_path();
1386 if(
nullptr == link_target_path || 0 == link_target_path->size() ) {
1387 ERR_PRINT(
"Symbolic link-path is empty %s", src_stats.toString().c_str());
1391 const int res = ::symlinkat(link_target_path->c_str(), dst_dirfd, dst_basename.c_str());
1395 jau::fprintf_td(stderr,
"copy: Ignored: Failed to create symink %s -> %s, %s, errno %d, %s\n",
1396 dst_basename.c_str(), link_target_path->c_str(), src_stats.toString().c_str(), errno, ::strerror(errno));
1400 ERR_PRINT(
"Creating symlink failed %s -> %s, %s", dst_basename.c_str(), link_target_path->c_str(), src_stats.toString().c_str());
1405 struct timespec ts2[2] = { src_stats.atime().to_timespec(), src_stats.mtime().to_timespec() };
1406 if( 0 != ::utimensat(dst_dirfd, dst_basename.c_str(), ts2, AT_SYMLINK_NOFOLLOW) ) {
1407 ERR_PRINT(
"Couldn't preserve time of symlink, source %s, dest '%s'", src_stats.toString().c_str(), dst_basename.c_str());
1411 const uid_t caller_uid = ::geteuid();
1412 const ::uid_t source_uid = 0 == caller_uid ? src_stats.uid() : -1;
1413 if( 0 != ::fchownat(dst_dirfd, dst_basename.c_str(), source_uid, src_stats.gid(), AT_SYMLINK_NOFOLLOW) ) {
1414 if( errno != EPERM && errno != EINVAL ) {
1415 ERR_PRINT(
"Couldn't preserve ownership of symlink, source %s, dest '%s'", src_stats.toString().c_str(), dst_basename.c_str());
1420 jau::fprintf_td(stderr,
"copy: Warn: Couldn't preserve ownership of symlink, source %s, dest '%s', errno %d (%s)\n",
1421 src_stats.toString().c_str(), dst_basename.c_str(), errno, ::strerror(errno));
1432 const uid_t caller_uid = ::geteuid();
1434 int src_flags = O_RDONLY|
O_BINARY|O_NOCTTY;
1435 uint64_t offset = 0;
1438#if defined(__linux__)
1439 if( caller_uid == target_stats->
uid() ) {
1440 src_flags |= O_NOATIME;
1443 src =
__posix_openat64(src_dirfd, src_stats.item().basename().c_str(), src_flags);
1445 if( src_stats.is_link() ) {
1449 ERR_PRINT(
"Failed to open source %s", src_stats.toString().c_str());
1451 jau::fprintf_td(stderr,
"copy: Ignored: Failed to open source %s, errno %d, %s\n", src_stats.toString().c_str(), errno, ::strerror(errno));
1457 ERR_PRINT(
"Failed to open target_path '%s'", dst_basename.c_str());
1460 while ( offset < src_stats.size()) {
1462#ifdef _USE_SENDFILE_
1463 off64_t offset_i = (off64_t)offset;
1464 const uint64_t count = std::max<uint64_t>(std::numeric_limits<ssize_t>::max(), src_stats.size() - offset);
1465 if( ( rc1 = ::sendfile64(dst, src, &offset_i, (
size_t)count) ) >= 0 ) {
1466 offset = (uint64_t)offset_i;
1469 char buffer[BUFSIZ];
1470 if( ( rc1 =
::read(src, buffer,
sizeof(buffer)) ) > 0 ) {
1471 ssize_t bytes_to_write = rc1;
1472 size_t buffer_offset = 0;
1473 while( 0 <= rc2 && 0 < bytes_to_write ) {
1474 while( ( rc2 =
::write(dst, buffer+buffer_offset, bytes_to_write) ) < 0 ) {
1475 if ( errno == EAGAIN || errno == EINTR ) {
1481 buffer_offset += rc2;
1482 bytes_to_write -= rc2;
1485 }
else if ( 0 > rc1 && ( errno == EAGAIN || errno == EINTR ) ) {
1490 if ( 0 > rc1 || 0 > rc2 ) {
1491#ifdef _USE_SENDFILE_
1492 ERR_PRINT(
"Failed to copy bytes @ %s / %s, %s -> '%s'",
1494 src_stats.toString().c_str(), dst_basename.c_str());
1497 ERR_PRINT(
"Failed to read bytes @ %s / %s, %s",
1499 src_stats.to_string().c_str());
1500 }
else if ( 0 > rc2 ) {
1501 ERR_PRINT(
"Failed to write bytes @ %s / %s, %s",
1503 dst_basename.c_str());
1512 if( offset < src_stats.size() ) {
1513 ERR_PRINT(
"Incomplete transfer %s / %s, %s -> '%s'",
1515 src_stats.toString().c_str(), dst_basename.c_str());
1522 ERR_PRINT(
"Couldn't restore omitted permissions, source %s, dest '%s'",
1523 src_stats.toString().c_str(), dst_basename.c_str());
1530 if( 0 != ::futimens(dst, ts2) ) {
1531 ERR_PRINT(
"Couldn't preserve time of file, source %s, dest '%s'",
1532 src_stats.toString().c_str(), dst_basename.c_str());
1536 ::uid_t source_uid = 0 == caller_uid ? target_stats->
uid() : -1;
1537 if( 0 != ::fchown(dst, source_uid, target_stats->
gid()) ) {
1538 if( errno != EPERM && errno != EINVAL ) {
1539 ERR_PRINT(
"Couldn't preserve ownership of file, uid(caller %" PRIu32
", chown %" PRIu32
"), source %s, dest '%s'",
1540 caller_uid, source_uid, src_stats.toString().c_str(), dst_basename.c_str());
1545 jau::fprintf_td(stderr,
"copy: Ignored: Preserve ownership of file failed, uid(caller %" PRIu32
", chown %" PRIu32
"), source %s, dest '%s', errno %d (%s)\n",
1546 caller_uid, source_uid, src_stats.toString().c_str(), dst_stats.
toString().c_str(), errno, ::strerror(errno));
1552 if( 0 != ::fsync(dst) ) {
1553 ERR_PRINT(
"Couldn't synchronize destination file, source %s, dest '%s'",
1554 src_stats.toString().c_str(), dst_basename.c_str());
1572 bool new_dir =
false;
1573 std::string basename_;
1574 const int dest_dirfd = ctx.dst_dirfds.back();
1575 if( dst_stats.is_dir() ) {
1577 jau::fprintf_td(stderr,
"copy: mkdir directory already exist: %s\n", dst_stats.toString().c_str());
1579 basename_.append( dst_stats.item().basename() );
1580 }
else if( !dst_stats.exists() ) {
1582 constexpr const int32_t val_min = 888;
1583 constexpr const int32_t val_max = std::numeric_limits<int32_t>::max();
1584 uint64_t mkdir_cntr = 0;
1585 std::mt19937_64 prng;
1586 std::uniform_int_distribution<int32_t> prng_dist(val_min, val_max);
1587 bool mkdir_ok =
false;
1590 const int32_t val_d = prng_dist(prng);
1595 }
else if (errno != EINTR && errno != EEXIST) {
1596 ERR_PRINT(
"mkdir failed: %s, temp '%s'", dst_stats.toString().c_str(), basename_.c_str());
1599 }
while( !mkdir_ok && mkdir_cntr < val_max );
1601 ERR_PRINT(
"mkdir failed: %s", dst_stats.toString().c_str());
1605 ERR_PRINT(
"mkdir failed: %s, exists but is no dir", dst_stats.toString().c_str());
1610 if ( 0 > new_dirfd ) {
1612 ERR_PRINT(
"Couldn't open new dir %s, temp '%s'", dst_stats.toString().c_str(), basename_.c_str());
1614 ERR_PRINT(
"Couldn't open new dir %s", dst_stats.toString().c_str());
1617 ::unlinkat(dest_dirfd, basename_.c_str(), AT_REMOVEDIR);
1624 ::unlinkat(dest_dirfd, basename_.c_str(), AT_REMOVEDIR);
1625 ERR_PRINT(
"zero permissions on dest %s, temp '%s'", dst_stats.toString().c_str(), basename_.c_str());
1627 ERR_PRINT(
"zero permissions on dest %s", dst_stats.toString().c_str());
1633#if defined(__linux__) && defined(__GLIBC__)
1634 const int rename_flags = 0;
1635 const int rename_res = ::renameat2(dest_dirfd, basename_.c_str(), dest_dirfd, dst_stats.item().basename().c_str(), rename_flags);
1637 const int rename_res = ::renameat(dest_dirfd, basename_.c_str(), dest_dirfd, dst_stats.item().basename().c_str());
1639 if( 0 != rename_res ) {
1640 ERR_PRINT(
"rename temp to dest, temp '%s', dest %s", basename_.c_str(), dst_stats.toString().c_str());
1641 ::unlinkat(dest_dirfd, basename_.c_str(), AT_REMOVEDIR);
1646 ctx.dst_dirfds.push_back(new_dirfd);
1651 const file_stats* target_stats = src_stats.
is_link() ? src_stats.link_target().get() : &src_stats;
1656 ERR_PRINT(
"restore permissions, source %s, dest '%s'", src_stats.toString().c_str(), dst_basename.c_str());
1663 if( 0 != ::futimens(dst_dirfd, ts2) ) {
1664 ERR_PRINT(
"preserve time of file failed, source %s, dest '%s'", src_stats.toString().c_str(), dst_basename.c_str());
1668 const uid_t caller_uid = ::geteuid();
1669 const ::uid_t source_uid = 0 == caller_uid ? target_stats->
uid() : -1;
1670 if( 0 != ::fchown(dst_dirfd, source_uid, target_stats->
gid()) ) {
1671 if( errno != EPERM && errno != EINVAL ) {
1672 ERR_PRINT(
"dir_preserve ownership of file failed, uid(caller %" PRIu32
", chown %" PRIu32
"), source %s, dest '%s'",
1673 caller_uid, source_uid, src_stats.toString().c_str(), dst_basename.c_str());
1678 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",
1679 caller_uid, source_uid, src_stats.toString().c_str(), dst_basename.c_str(), errno, ::strerror(errno));
1684 if( 0 != ::fsync(dst_dirfd) ) {
1685 ERR_PRINT(
"Couldn't synchronize destination file '%s'", dst_basename.c_str());
1706 if( source_stats.
is_file() ) {
1710 if( target_stats.
exists() ) {
1711 if( target_stats.
is_file() ) {
1714 jau::fprintf_td(stderr,
"copy: Error: source_path is file, target_path existing file w/o overwrite, source %s, target %s\n",
1722 if ( 0 > src_dirfd ) {
1723 ERR_PRINT(
"source_path dir couldn't be opened, source %s", source_stats.
toString().c_str());
1727 std::string dst_basename;
1729 if( target_stats.
is_dir() ) {
1732 if ( 0 > dst_dirfd ) {
1733 ERR_PRINT(
"target dir couldn't be opened, target %s", target_stats.
toString().c_str());
1741 if( !target_parent_stats.
is_dir() ) {
1743 jau::fprintf_td(stderr,
"copy: Error: target parent is not an existing directory, target %s, target_parent %s\n",
1750 if ( 0 > dst_dirfd ) {
1751 ERR_PRINT(
"target_parent dir couldn't be opened, target %s, target_parent %s",
1758 if( !
copy_file(src_dirfd, source_stats, dst_dirfd, dst_basename, copts) ) {
1765 if( !source_stats.
is_dir() ) {
1767 jau::fprintf_td(stderr,
"copy: Error: source_path is neither file nor dir, source %s, target %s\n",
1776 copy_context_t ctx { .copts=copts, .skip_dst_dir_mkdir=0, .src_dirfds=std::vector<int>(), .dst_dirfds=std::vector<int>() };
1780 jau::fprintf_td(stderr,
"copy: Error: source_path is dir but !recursive, %s\n", source_stats.
toString().c_str());
1784 if( target_stats.
exists() && !target_stats.
is_dir() ) {
1786 jau::fprintf_td(stderr,
"copy: Error: source_path is dir but target_path exist and is no dir, source %s, target %s\n",
1796 if ( 0 > dst_dirfd ) {
1797 ERR_PRINT(
"target dir couldn't be opened, target %s", target_stats.
toString().c_str());
1800 ctx.dst_dirfds.push_back(dst_dirfd);
1805 if( !target_parent_stats.
is_dir() ) {
1807 jau::fprintf_td(stderr,
"copy: Error: target parent is not an existing directory, target %s, target_parent %s\n",
1813 if ( 0 > dst_parent_dirfd ) {
1814 ERR_PRINT(
"target dirname couldn't be opened, target %s, target_parent %s",
1818 ctx.dst_dirfds.push_back(dst_parent_dirfd);
1820 if( target_stats.
is_dir() ) {
1823 if ( 0 > dst_dirfd ) {
1824 ERR_PRINT(
"target dir couldn't be opened, target %s", target_stats.
toString().c_str());
1827 ctx.dst_dirfds.push_back(dst_dirfd);
1833 ctx.skip_dst_dir_mkdir = 1;
1846 ERR_PRINT(
"dirfd stack error: count[src %zu, dst %zu, dst_skip %d] @ %s",
1850 const int src_dirfd = ctx_ptr->
src_dirfds.back();
1851 const int dst_dirfd = ctx_ptr->
dst_dirfds.back();
1852 const std::string& basename_ = element_stats.
item().
basename();
1857 const file_stats target_stats_(dst_dirfd, basename_);
1864 ERR_PRINT(
"dirfd stack error: count[src %zu, dst %zu] @ %s",
1874 if( !
copy_file(src_dirfd, element_stats, dst_dirfd, basename_, ctx_ptr->
copts) ) {
1881 while( !ctx.dst_dirfds.empty() ) {
1882 ::close(ctx.dst_dirfds.back());
1883 ctx.dst_dirfds.pop_back();
1892 ERR_PRINT(
"oldpath doesn't exist, oldpath %s, newpath %s\n",
1896 if( 0 !=
::rename(oldpath_stats.
path().c_str(), newpath_stats.
path().c_str()) ) {
1897 ERR_PRINT(
"rename failed, oldpath %s, newpath %s\n",
1909 if( 0 != ::seteuid(user_id) ) {
1910 ERR_PRINT(
"seteuid(%" PRIu32
") failed", user_id);
1917 const mountflags_t flags,
const std::string& fs_options)
1925 if( !target_stats.
is_dir()) {
1929 const std::string target_path(target_stats.
path());
1931 if( 0 > backingfile ) {
1932 ERR_PRINT(
"Couldn't open image-file '%s': res %d", image_stats.
toString().c_str(), backingfile);
1935#if defined(__linux__)
1936 const ::uid_t caller_uid = ::geteuid();
1937 int loop_device_id = -1;
1939 ::pid_t pid = ::fork();
1941 int loop_ctl_fd = -1, loop_device_fd = -1;
1942 char loopname[4096];
1944 void* fs_options_cstr =
nullptr;
1946 if( 0 != caller_uid ) {
1952 if( 0 > loop_ctl_fd ) {
1953 ERR_PRINT(
"Couldn't open loop-control: res %d", loop_ctl_fd);
1957 loop_device_id = ::ioctl(loop_ctl_fd, LOOP_CTL_GET_FREE);
1958 if( 0 > loop_device_id ) {
1959 ERR_PRINT(
"Couldn't get free loop-device: res %d", loop_device_id);
1962 if( 254 < loop_device_id ) {
1963 ERR_PRINT(
"loop-device %d out of valid range [0..254]", loop_device_id);
1966 ::close(loop_ctl_fd);
1969 snprintf(loopname,
sizeof(loopname),
"/dev/loop%d", loop_device_id);
1973 if( 0 > loop_device_fd ) {
1974 ERR_PRINT(
"Couldn't open loop-device '%s': res %d", loopname, loop_device_fd);
1977 if( 0 > ::ioctl(loop_device_fd, LOOP_SET_FD, backingfile) ) {
1978 ERR_PRINT(
"Couldn't attach image-file '%s' to loop-device '%s'", image_stats.
toString().c_str(), loopname);
1982 if( fs_options.size() > 0 ) {
1983 fs_options_cstr = (
void*) fs_options.data();
1985 mount_res =
::mount(loopname, target_path.c_str(), fs_type.c_str(), flags, fs_options_cstr);
1986 if( 0 != mount_res ) {
1987 ERR_PRINT(
"source_path %s, target_path %s, fs_type %s, res %d",
1988 image_stats.
path().c_str(), target_path.c_str(), fs_type.c_str(), mount_res);
1989 ::ioctl(loop_device_fd, LOOP_CLR_FD, 0);
1992 ::close(loop_device_fd);
1993 ::_exit(loop_device_id+1);
1996 if( 0 <= loop_ctl_fd ) {
1997 ::close(loop_ctl_fd);
1999 if( 0 <= loop_device_fd ) {
2000 ::close(loop_device_fd);
2003 }
else if( 0 < pid ) {
2005 ::pid_t child_pid = ::waitpid(pid, &pid_status, 0);
2006 if( 0 > child_pid ) {
2007 ERR_PRINT(
"wait(%d) failed: child_pid %d", pid, child_pid);
2009 if( child_pid != pid ) {
2010 WARN_PRINT(
"wait(%d) terminated child_pid %d", pid, child_pid);
2012 if( !WIFEXITED(pid_status) ) {
2013 WARN_PRINT(
"wait(%d) terminated abnormally child_pid %d, pid_status %d", pid, child_pid, pid_status);
2016 loop_device_id = WEXITSTATUS(pid_status);
2017 if( 0 >= loop_device_id ) {
2023 ERR_PRINT(
"Couldn't fork() process: res %d", pid);
2026 if( 0 <= backingfile ) {
2027 ::close(backingfile);
2029 return mount_ctx(target_path, loop_device_id);
2037 if( 0 <= backingfile ) {
2038 ::close(backingfile);
2044 const mountflags_t flags,
const std::string& fs_options)
2046 if( source.empty() ) {
2047 ERR_PRINT(
"source is an empty string ");
2052 if( !target_stats.
is_dir()) {
2056 const std::string target_path(target_stats.
path());
2057 const ::uid_t caller_uid = ::geteuid();
2059 ::pid_t pid = ::fork();
2061 void* fs_options_cstr =
nullptr;
2063 if( 0 != caller_uid ) {
2065 ::_exit( EXIT_FAILURE );
2068 if( fs_options.size() > 0 ) {
2069 fs_options_cstr = (
void*) fs_options.data();
2071#if defined(__linux__)
2072 const int mount_res =
::mount(source_stats.
path().c_str(), target_path.c_str(), fs_type.c_str(), flags, fs_options_cstr);
2073#elif defined(__FreeBSD__)
2080 (void)fs_options_cstr;
2081 const int mount_res = -1;
2083 #if !defined(JAU_OS_TYPE_WASM)
2084 #warning Add OS support
2087 (void)fs_options_cstr;
2088 const int mount_res = -1;
2090 if( 0 != mount_res ) {
2091 ERR_PRINT(
"source_path %s, target_path %s, fs_type %s, flags %" PRIu64
", res %d",
2092 source_stats.
path().c_str(), target_path.c_str(), fs_type.c_str(), flags, mount_res);
2093 ::_exit( EXIT_FAILURE );
2095 ::_exit( EXIT_SUCCESS );
2098 }
else if( 0 < pid ) {
2100 ::pid_t child_pid = ::waitpid(pid, &pid_status, 0);
2101 if( 0 > child_pid ) {
2102 ERR_PRINT(
"wait(%d) failed: child_pid %d", pid, child_pid);
2104 if( child_pid != pid ) {
2105 WARN_PRINT(
"wait(%d) terminated child_pid %d", pid, child_pid);
2107 if( !WIFEXITED(pid_status) ) {
2108 WARN_PRINT(
"wait(%d) terminated abnormally child_pid %d, pid_status %d", pid, child_pid, pid_status);
2109 }
else if( EXIT_SUCCESS == WEXITSTATUS(pid_status) ) {
2114 ERR_PRINT(
"Couldn't fork() process: res %d", pid);
2125 if( !target_stats.
is_dir()) {
2128 const ::uid_t caller_uid = ::geteuid();
2130 ::pid_t pid = ::fork();
2132 if( 0 != caller_uid ) {
2134 ::_exit( EXIT_FAILURE );
2137#if defined(__linux__)
2138 const int umount_res = ::umount2(target_stats.
path().c_str(), flags);
2139#elif defined(__FreeBSD__)
2140 const int umount_res = ::unmount(target_stats.
path().c_str(), flags);
2142 #if !defined(JAU_OS_TYPE_WASM)
2143 #warning Add OS support
2145 const int umount_res = -1;
2147 if( 0 != umount_res ) {
2148 ERR_PRINT(
"Couldn't umount '%s', flags %d: res %d\n", target_stats.
toString().c_str(), flags, umount_res);
2152 ::_exit(0 == umount_res ? EXIT_SUCCESS : EXIT_FAILURE);
2154#if defined(__linux__)
2155 int loop_device_fd = -1;
2156 char loopname[4096];
2158 snprintf(loopname,
sizeof(loopname),
"/dev/loop%d", context.
loop_device_id);
2162 if( 0 > loop_device_fd ) {
2163 ERR_PRINT(
"Couldn't open loop-device '%s': res %d", loopname, loop_device_fd);
2166 if( 0 > ::ioctl(loop_device_fd, LOOP_CLR_FD, 0) ) {
2167 ERR_PRINT(
"Couldn't detach loop-device '%s'", loopname);
2170 ::close(loop_device_fd);
2171 ::_exit(0 == umount_res ? EXIT_SUCCESS : EXIT_FAILURE);
2175 ::_exit( EXIT_FAILURE );
2177#if defined(__linux__)
2179 if( 0 <= loop_device_fd ) {
2180 ::close(loop_device_fd);
2182 ::_exit( EXIT_FAILURE );
2184 }
else if( 0 < pid ) {
2186 ::pid_t child_pid = ::waitpid(pid, &pid_status, 0);
2187 if( 0 > child_pid ) {
2188 ERR_PRINT(
"wait(%d) failed: child_pid %d", pid, child_pid);
2190 if( child_pid != pid ) {
2191 WARN_PRINT(
"wait(%d) terminated child_pid %d", pid, child_pid);
2193 if( !WIFEXITED(pid_status) ) {
2194 WARN_PRINT(
"wait(%d) terminated abnormally child_pid %d, pid_status %d", pid, child_pid, pid_status);
2195 }
else if( EXIT_SUCCESS == WEXITSTATUS(pid_status) ) {
2200 ERR_PRINT(
"Couldn't fork() process: res %d", pid);
2207 if( target.empty() ) {
2211 if( !target_stats.
is_dir()) {
2214 const ::uid_t caller_uid = ::geteuid();
2216 ::pid_t pid = ::fork();
2218 if( 0 != caller_uid ) {
2220 ::_exit( EXIT_FAILURE );
2223#if defined(__linux__)
2224 const int umount_res = ::umount2(target_stats.
path().c_str(), flags);
2225#elif defined(__FreeBSD__)
2226 const int umount_res = ::unmount(target_stats.
path().c_str(), flags);
2228 #if !defined(JAU_OS_TYPE_WASM)
2229 #warning Add OS support
2231 const int umount_res = -1;
2233 if( 0 == umount_res ) {
2234 ::_exit( EXIT_SUCCESS );
2236 ERR_PRINT(
"Couldn't umount '%s', flags %d: res %d\n", target_stats.
toString().c_str(), flags, umount_res);
2237 ::_exit( EXIT_FAILURE );
2239 }
else if( 0 < pid ) {
2241 ::pid_t child_pid = ::waitpid(pid, &pid_status, 0);
2242 if( 0 > child_pid ) {
2243 ERR_PRINT(
"wait(%d) failed: child_pid %d", pid, child_pid);
2245 if( child_pid != pid ) {
2246 WARN_PRINT(
"wait(%d) terminated child_pid %d", pid, child_pid);
2248 if( !WIFEXITED(pid_status) ) {
2249 WARN_PRINT(
"wait(%d) terminated abnormally child_pid %d, pid_status %d", pid, child_pid, pid_status);
2250 }
else if( EXIT_SUCCESS == WEXITSTATUS(pid_status) ) {
2255 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 & dirname() const noexcept
Returns the dirname, shall not be empty and denotes .
const std::string & basename() const noexcept
Return the basename, shall not be empty nor contain a dirname.
dir_item() noexcept
Empty item w/ .
std::string toString() const noexcept
Returns a comprehensive string representation of this item.
Platform agnostic representation of POSIX ::lstat() and ::stat() for a given pathname.
constexpr bool is_dir() const noexcept
Returns true if entity is a directory, might be in combination with is_link().
constexpr bool is_file() const noexcept
Returns true if entity is a file, might be in combination with is_link().
const fraction_timespec & mtime() const noexcept
Returns the last modification time of this element since Unix Epoch.
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 path() const noexcept
Returns the unix path representation.
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...
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.
fmode_t prot_mode() const noexcept
Returns the POSIX protection bit portion of fmode_t, i.e.
constexpr bool has_access() const noexcept
Returns true if entity allows access to user, exclusive bit.
file_stats() noexcept
Instantiate an empty file_stats with fmode_t::not_existing set.
field_t
Field identifier which bit-mask indicates field_t fields.
@ mode
POSIX file protection mode bits.
@ fd
A generic file-descriptor not necessarily a file, possible pipe etc.
@ type
File type mode bits.
uid_t uid() const noexcept
Returns the user id, owning the element.
std::string toString() const noexcept
Returns a comprehensive string representation of this element.
constexpr bool is_link() const noexcept
Returns true if entity is a symbolic link, might be in combination with is_file(),...
int fd() const noexcept
Returns the file descriptor if has_fd(), otherwise -1 for no file descriptor.
constexpr field_t fields() const noexcept
Returns the retrieved field_t fields.
constexpr bool exists() const noexcept
Returns true if entity does not exist, exclusive bit.
bool operator==(const file_stats &rhs) const noexcept
const fraction_timespec & atime() const noexcept
Returns the last access time of this element since Unix Epoch.
constexpr bool has_fd() const noexcept
Returns true if entity has a file descriptor.
gid_t gid() const noexcept
Returns the group id, owning the element.
#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 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 umount(const mount_ctx &context, const umountflags_t flags)
Detach the given mount_ctx context
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.
std::string absolute(const std::string_view &relpath) noexcept
Returns the absolute path of given relpath if existing, otherwise an empty string.
int from_named_fd(const std::string &named_fd) noexcept
Returns the file descriptor from the given named file descriptor.
traverse_event
Filesystem traverse event used to call path_visitor for path elements from visit().
std::string basename(const std::string_view &path) noexcept
Return stripped leading directory components from given path separated by /.
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 dirname(const std::string_view &path) noexcept
Return stripped last component from given path separated by /, excluding the trailing separator /.
int umountflags_t
Generic flag bit values for umount() flags.
bool touch(const std::string &path, const jau::fraction_timespec &atime, const jau::fraction_timespec &mtime, const fmode_t mode=fmode_t::def_file_prot) noexcept
Touch the file with given atime and mtime and create file if not existing yet.
bool isAbsolute(const std::string_view &path) noexcept
Returns true if first character is / or - in case of Windows - \\.
fmode_t
Generic file type and POSIX protection mode bits as used in file_stats, touch(), mkdir() etc.
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...
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.
copy_options
Filesystem copy options used to copy() path elements.
std::string get_cwd() noexcept
Return the current working directory or empty on failure.
std::string to_string(const fmode_t mask, const bool show_rwx) noexcept
Return the string representation of fmode_t.
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.
traverse_options
Filesystem traverse options used to visit() path elements.
void sync() noexcept
Synchronizes filesystems, 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=fmode_t::def_dir_prot, const bool verbose=false) noexcept
Create directory.
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 chdir(const std::string &path) noexcept
Change working directory.
std::string to_named_fd(const int fd) noexcept
Returns platform dependent named file descriptor of given file descriptor, if supported.
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.
jau::function< bool(traverse_event, const file_stats &, size_t)> path_visitor
path_visitor jau::FunctionDef definition
uint64_t mountflags_t
Generic flag bit values for mount() flags.
bool get_dir_content(const std::string &path, const consume_dir_item &digest) noexcept
Returns a list of directory elements excluding .
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.
bool rename(const std::string &oldpath, const std::string &newpath) noexcept
Rename oldpath to newpath using POSIX rename(), with the following combinations.
@ 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.
@ 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.
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::string to_string(const bit_order_t v) noexcept
Return std::string representation of the given bit_order_t.
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...