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) );
174std::string
jau::fs::basename(
const std::string_view& path,
const std::string_view& suffix)
noexcept {
176 if( res.length() < suffix.length() ) {
179 size_t n = res.length() - suffix.length();
180 size_t idx = res.find(suffix, n);
181 if( idx == std::string_view::npos ) {
184 return res.substr(0, n);
188std::string
jau::fs::basename(
const std::string_view& path,
const std::vector<std::string_view>& suffixes)
noexcept {
190 for(std::string_view suffix : suffixes) {
191 if( res.length() >= suffix.length() ) {
192 size_t n = res.length() - suffix.length();
193 size_t idx = res.find(suffix, n);
194 if( idx != std::string_view::npos ) {
195 return res.substr(0, n);
203 return path.size() > 0 &&
209 ::memset(&s, 0,
sizeof(s));
212 if( 0 != stat_res && verbose_on_error ) {
213 fprintf(stderr,
"exists '%s': %d: %d %s\n", path.c_str(), stat_res, errno, strerror(errno));
215 return 0 == stat_res;
223 std::string assetdir0 =
"resources";
224 if(
exists( assetdir0+
"/"+asset_file ) ) {
227 if( !exe_path || !asset_install_subdir ) {
234 std::string assetdir1 =
jau::fs::absolute( adir+
"/../share/"+asset_install_subdir );
235 if(
exists( assetdir1+
"/"+asset_file ) ) {
238 fprintf(stderr,
"asset_dir: Not found: dir '%s', file '%s', exe[path '%s', dir '%s'], cwd '%s', adir '%s'\n",
239 assetdir1.c_str(), asset_file, exe_path, exedir.c_str(), cwd.c_str(), adir.c_str());
243std::unique_ptr<dir_item::backed_string_view> dir_item::reduce(
const std::string_view& path_)
noexcept {
244 constexpr const bool _debug =
false;
246 if constexpr ( _debug ) {
247 jau::fprintf_td(stderr,
"X.0: path '%s'\n", std::string(path_).c_str());
250 std::unique_ptr<dir_item::backed_string_view> path2 = std::make_unique<dir_item::backed_string_view>(path_);
258 path2->view = path2->view.substr(2, path2->view.size()-2);
262 if(
c_slash == path2->view[path2->view.size()-1] &&
263 ( path2->view.size() < 3 || std::string_view::npos == path2->view.find(
s_slash_dot_slash, path2->view.size()-3) ) &&
264 ( path2->view.size() < 4 || std::string_view::npos == path2->view.find(
s_slash_dotdot_slash, path2->view.size()-4) )
267 path2->view = path2->view.substr(0, path2->view.size()-1);
271 if( ( path2->view.size() >= 3 && std::string_view::npos != path2->view.find(
s_slash_dotdot, path2->view.size()-3) ) ||
272 ( path2->view.size() >= 2 && std::string_view::npos != path2->view.find(
s_slash_dot, path2->view.size()-2) ) ) {
274 path2->view = path2->backing.append(
s_slash);
277 if constexpr ( _debug ) {
278 jau::fprintf_td(stderr,
"X.1: path2 '%s'\n", path2->to_string(
true).c_str());
286 if constexpr ( _debug ) {
287 jau::fprintf_td(stderr,
"X.2.1: path2: spos %zu, idx %zu, '%s'\n", spos, idx, path2->to_string(
true).c_str());
289 if( std::string_view::npos == idx ) {
292 std::string_view pre = path2->view.substr(0, idx);
293 if( 0 == pre.size() ) {
295 path2->view = path2->view.substr(idx+2);
299 const std::string post( path2->view.substr(idx+2) );
300 path2->backup_and_append( pre, post );
303 if constexpr ( _debug ) {
304 jau::fprintf_td(stderr,
"X.2.2: path2: spos %zu, '%s'\n", spos, path2->to_string(
true).c_str());
306 }
while( spos <= path2->view.size()-3 );
307 if constexpr ( _debug ) {
308 jau::fprintf_td(stderr,
"X.2.X: path2: '%s'\n", path2->to_string(
true).c_str());
315 if constexpr ( _debug ) {
316 jau::fprintf_td(stderr,
"X.3.1: path2: spos %zu, idx %zu, '%s'\n", spos, idx, path2->to_string(
true).c_str());
318 if( std::string_view::npos == idx ) {
323 WARN_PRINT(
"dir_item::resolve: '..' resolution error: '%s' -> '%s'", std::string(path_).c_str(), path2->to_string().c_str());
326 std::string_view pre = path2->view.substr(0, idx);
330 }
else if( 3 <= idx &&
s_slash_dotdot == path2->view.substr(idx-3, 3) ) {
337 path2->view = path2->view.substr(idx+3);
339 }
else if(
s_dot == pre_str ) {
341 path2->view = path2->view.substr(idx+4);
345 const std::string post( path2->view.substr(idx+3) );
346 path2->backup_and_append( pre_str, post );
347 spos = pre_str.size();
350 if constexpr ( _debug ) {
351 jau::fprintf_td(stderr,
"X.3.2: path2: spos %zu, '%s'\n", spos, path2->to_string(
true).c_str());
353 }
while( spos <= path2->view.size()-4 );
354 if constexpr ( _debug ) {
355 jau::fprintf_td(stderr,
"X.3.X: path2: '%s'\n", path2->to_string(
true).c_str());
359 if(
c_slash == path2->view[path2->view.size()-1] ) {
360 path2->view = path2->view.substr(0, path2->view.size()-1);
362 if constexpr ( _debug ) {
363 jau::fprintf_td(stderr,
"X.X: path2: '%s'\n", path2->to_string(
true).c_str());
376: dirname_(std::move(dirname__)), basename_(std::move(basename__)), empty_(dirname_.empty() && basename_.empty()) {
380: dirname_(
s_dot), basename_(
s_dot), empty_(true) {}
383: dir_item( reduce(path_) )
388 if(
s_dot == dirname_ ) {
391 if(
s_dot == basename_ ) {
395 return dirname_ + basename_;
397 return dirname_ +
s_slash + basename_;
406 if(
is_set( mask, bit )) {
423 const std::string r(
"r");
424 const std::string w(
"w");
425 const std::string x(
"x");
438 out.append(std::string(buf, len));
448 std::string res(
"/dev/fd/");
449 res.append(std::to_string(fd));
455 if( 1 == sscanf(named_fd.c_str(),
"/dev/fd/%d", &scan_value) ) {
458 }
else if( 1 == sscanf(named_fd.c_str(),
"/proc/self/fd/%d", &scan_value) ) {
467 uid_(0), gid_(0), size_(0), btime_(), atime_(), ctime_(), mtime_(),
472 static constexpr bool jau_has_stat(
const uint32_t mask,
const uint32_t bit) {
return bit == ( mask & bit ); }
477 uid_(0), gid_(0), size_(0), btime_(), atime_(), ctime_(), mtime_(), errno_res_(0)
479 constexpr const bool _debug =
false;
481 const std::string full_path(
item.empty() ?
"" :
item.path() );
482 if(
item.empty() && AT_FDCWD != dirfd ) {
488 ERR_PRINT(
"rec_level %d, dirfd %d < 0, %s, dirfd_is_item_dirname %d, AT_EMPTY_PATH",
489 (
int)cc.rec_level, dirfd,
item.to_string().c_str(), dirfd_is_item_dirname);
495 if( 0 <= scan_value ) {
499 }
else if( full_path.starts_with(
"/dev/fd/pipe:") ) {
507 if constexpr ( _debug ) {
508 jau::fprintf_td(stderr,
"file_stats(%d): FIFO: '%s', errno %d (%s)\n", (
int)cc.rec_level,
to_string().c_str(), errno, ::strerror(errno));
513 const std::string dirfd_path =
has(
field_t::fd ) ?
"" : ( dirfd_is_item_dirname ? item_.basename() : full_path );
518 int stat_res = ::statx(dirfd, dirfd_path.c_str(),
519 ( AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | (
has(
field_t::fd ) ? AT_EMPTY_PATH : 0 ) ),
520 ( STATX_BASIC_STATS | STATX_BTIME ), &s);
521 if( 0 != stat_res ) {
522 if constexpr ( _debug ) {
523 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));
539 if( jau_has_stat( s.stx_mask, STATX_TYPE ) ) {
543 if( S_ISLNK( s.stx_mode ) ) {
546 if( S_ISREG( s.stx_mode ) ) {
548 }
else if( S_ISDIR( s.stx_mode ) ) {
550 }
else if( S_ISFIFO( s.stx_mode ) ) {
552 }
else if( S_ISCHR( s.stx_mode ) ) {
554 }
else if( S_ISSOCK( s.stx_mode ) ) {
556 }
else if( S_ISBLK( s.stx_mode ) ) {
560 if( jau_has_stat( s.stx_mask, STATX_MODE ) ) {
563 mode_ |=
static_cast<fmode_t>( s.stx_mode & ( S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX ) );
565 if( jau_has_stat( s.stx_mask, STATX_NLINK ) ) {
568 if( jau_has_stat( s.stx_mask, STATX_UID ) ) {
572 if( jau_has_stat( s.stx_mask, STATX_GID ) ) {
576 if( jau_has_stat( s.stx_mask, STATX_ATIME ) || 0 != s.stx_atime.tv_sec || 0 != s.stx_atime.tv_nsec ) {
580 if( jau_has_stat( s.stx_mask, STATX_MTIME ) ) {
584 if( jau_has_stat( s.stx_mask, STATX_CTIME ) ) {
588 if( jau_has_stat( s.stx_mask, STATX_INO ) ) {
591 if( jau_has_stat( s.stx_mask, STATX_SIZE ) ) {
597 if( jau_has_stat( s.stx_mask, STATX_BLOCKS ) ) {
600 if( jau_has_stat( s.stx_mask, STATX_BTIME ) ) {
606 std::string link_path;
608 const size_t path_link_max_len = 0 < s.stx_size ? s.stx_size + 1 : PATH_MAX;
609 std::vector<char> buffer;
610 buffer.reserve(path_link_max_len);
611 buffer.resize(path_link_max_len);
612 const ssize_t path_link_len = ::readlinkat(dirfd, dirfd_path.c_str(), buffer.data(), path_link_max_len);
613 if( 0 > path_link_len ) {
615 link_target_ = std::make_shared<file_stats>();
619 link_path = std::string(buffer.data(), path_link_len);
621 link_target_path_ = std::make_shared<std::string>(link_path);
622 if( 0 == cc.rec_level ) {
625 stat_res = ::statx(dirfd, dirfd_path.c_str(), AT_NO_AUTOMOUNT | (
has(
field_t::fd ) ? AT_EMPTY_PATH : 0 ), STATX_BASIC_STATS, &s);
626 if( 0 != stat_res ) {
627 if constexpr ( _debug ) {
628 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));
648 if( 0 < link_path.size() &&
c_slash == link_path[0] ) {
649 link_target_ = std::make_shared<file_stats>(ctor_cookie(cc.rec_level+1), dirfd,
dir_item( link_path ),
false );
651 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 );
653 if( link_target_->has_fd() ) {
655 fd_ = link_target_->fd();
657 if( link_target_->is_socket() ) {
659 }
else if( link_target_->is_block() ) {
661 }
else if( link_target_->is_char() ) {
663 }
else if( link_target_->is_fifo() ) {
665 }
else if( link_target_->is_dir() ) {
667 }
else if( link_target_->is_file() ) {
671 size_ = link_target_->size();
673 }
else if( !link_target_->exists() ) {
675 }
else if( !link_target_->has_access() ) {
679 if constexpr ( _debug ) {
680 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));
687 if( 0 != stat_res ) {
688 if constexpr ( _debug ) {
689 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));
708 if( S_ISLNK( s.st_mode ) ) {
711 if( S_ISREG( s.st_mode ) ) {
717 }
else if( S_ISDIR( s.st_mode ) ) {
719 }
else if( S_ISFIFO( s.st_mode ) ) {
721 }
else if( S_ISCHR( s.st_mode ) ) {
723 }
else if( S_ISSOCK( s.st_mode ) ) {
725 }
else if( S_ISBLK( s.st_mode ) ) {
730 mode_ |=
static_cast<fmode_t>( s.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX ) );
740 std::string link_path;
742 const size_t path_link_max_len = 0 < s.st_size ? s.st_size + 1 : PATH_MAX;
743 std::vector<char> buffer;
744 buffer.reserve(path_link_max_len);
745 buffer.resize(path_link_max_len);
746 const ssize_t path_link_len = ::readlinkat(dirfd, dirfd_path.c_str(), buffer.data(), path_link_max_len);
747 if( 0 > path_link_len ) {
749 link_target_ = std::make_shared<file_stats>();
753 link_path = std::string(buffer.data(), path_link_len);
755 link_target_path_ = std::make_shared<std::string>(link_path);
756 if( 0 == cc.rec_level ) {
760 if( 0 != stat_res ) {
761 if constexpr ( _debug ) {
762 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));
782 if( 0 < link_path.size() &&
c_slash == link_path[0] ) {
783 link_target_ = std::make_shared<file_stats>(ctor_cookie(cc.rec_level+1), dirfd,
dir_item( link_path ),
false );
785 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 );
787 if( link_target_->has_fd() ) {
789 fd_ = link_target_->fd();
791 if( link_target_->is_socket() ) {
793 }
else if( link_target_->is_block() ) {
795 }
else if( link_target_->is_char() ) {
797 }
else if( link_target_->is_fifo() ) {
799 }
else if( link_target_->is_dir() ) {
801 }
else if( link_target_->is_file() ) {
804 size_ = link_target_->size();
805 }
else if( !link_target_->exists() ) {
807 }
else if( !link_target_->has_access() ) {
811 if constexpr ( _debug ) {
812 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));
844 while(
nullptr != fs1 ) {
849 if(
nullptr != link_count ) {
863 return item_ == rhs.item_ &&
864 has_fields_ == rhs.has_fields_ &&
865 mode_ == rhs.mode_ &&
866 uid_ == rhs.uid_ && gid_ == rhs.gid_ &&
867 errno_res_ == rhs.errno_res_ &&
868 size_ == rhs.size_ &&
869 btime_ == rhs.btime_ &&
870 atime_ == rhs.atime_ &&
871 ctime_ == rhs.ctime_ &&
872 mtime_ == rhs.mtime_ &&
874 ( link_target_path_ == rhs.link_target_path_&&
875 link_target_ == rhs.link_target_
881 std::string stored_path, link_detail;
883 if(
nullptr != link_target_path_ ) {
884 stored_path =
" [-> "+*link_target_path_+
"]";
888 if( 0 < link_count ) {
889 link_detail =
" -(" + std::to_string(link_count) +
")-> '" + final_target_->
path() +
"'";
892 std::string res(
"file_stats[");
894 .append(
", '"+item_.path()+
"'"+stored_path+link_detail );
895 if( 0 == errno_res_ ) {
897 res.append(
", fd " ).append( std::to_string(fd_) );
900 res.append(
", uid " ).append( std::to_string(uid_) );
903 res.append(
", gid " ).append( std::to_string(gid_) );
908 res.append(
", size n/a" );
911 res.append(
", btime " ).append( btime_.to_iso8601_string() );
914 res.append(
", atime " ).append( atime_.to_iso8601_string() );
917 res.append(
", ctime " ).append( ctime_.to_iso8601_string() );
920 res.append(
", mtime " ).append( mtime_.to_iso8601_string() );
924 res.append(
", errno " ).append( std::to_string(errno_res_) ).append(
", " ).append( std::string(::strerror(errno_res_)) );
938 }
else if( !stats.
exists() ) {
940 if ( 0 != dir_err ) {
955 ERR_PRINT(
"Couldn't open/create file '%s'", path.c_str());
958 struct timespec ts2[2] = { atime.to_timespec(), mtime.to_timespec() };
960 if( 0 != ::futimens(fd, ts2) ) {
961 ERR_PRINT(
"Couldn't update time of file '%s'", path.c_str());
973 ERR_PRINT(
"Couldn't open/create file '%s'", path.c_str());
977 if( 0 != ::futimens(fd,
nullptr ) ) {
978 ERR_PRINT(
"Couldn't update time of file '%s'", path.c_str());
991 if( (
dir = ::opendir( path.c_str() ) ) !=
nullptr ) {
992 while ( ( ent = ::readdir(
dir ) ) !=
nullptr ) {
993 std::string fname( ent->d_name );
1008 int dirfd2 = ::dup(dirfd);
1010 ERR_PRINT(
"Couldn't duplicate given dirfd %d for path '%s'", dirfd, path.c_str());
1013 if( (
dir = ::fdopendir( dirfd2 ) ) !=
nullptr ) {
1014 while ( ( ent = ::readdir(
dir ) ) !=
nullptr ) {
1015 std::string fname( ent->d_name );
1032 const size_t depth = dirfds.size();
1033 if( item_stats.is_dir() ) {
1040 if( dirfds.size() < 1 ) {
1041 ERR_PRINT(
"dirfd stack error: count %zu] @ %s", dirfds.size(), item_stats.to_string().c_str());
1044 const int parent_dirfd = dirfds.back();
1046 if ( 0 > this_dirfd ) {
1047 ERR_PRINT(
"entered path dir couldn't be opened, source %s", item_stats.to_string().c_str());
1050 dirfds.push_back(this_dirfd);
1054 ::close(this_dirfd);
1061 ::close(this_dirfd);
1066 std::vector<dir_item> content;
1068 ( void(*)(std::vector<dir_item>*,
const dir_item&) )
1069 ( [](std::vector<dir_item>* receiver,
const dir_item& item) ->
void { receiver->push_back( item ); } )
1071 if(
get_dir_content(this_dirfd, item_stats.path(), cs) && content.size() > 0 ) {
1075 for (
const dir_item& element : content) {
1076 const file_stats element_stats( this_dirfd, element,
true );
1077 if( element_stats.
is_dir() ) {
1080 ::close(this_dirfd);
1084 }
else if( !
_visit(element_stats, topts, visitor, dirfds) ) {
1085 ::close(this_dirfd);
1091 element_stats, depth ) )
1093 ::close(this_dirfd);
1099 if( dirfds.size() < 2 ) {
1100 ERR_PRINT(
"dirfd stack error: count %zu] @ %s", dirfds.size(), item_stats.to_string().c_str());
1107 ::close(this_dirfd);
1111 else if( item_stats.is_file() || !item_stats.ok() ) {
1120 const bool user_dirfds =
nullptr != dirfds;
1121 if( !user_dirfds ) {
1123 dirfds =
new std::vector<int>();
1124 }
catch (
const std::bad_alloc &e) {
1125 ABORT(
"Error: bad_alloc: dirfds allocation failed");
1129 if( 0 != dirfds->size() ) {
1130 ERR_PRINT(
"dirfd stack error: count %zu @ %s", dirfds->size(), item_stats.to_string().c_str());
1136 ERR_PRINT(
"path dirname couldn't be opened, source %s", item_stats.to_string().c_str());
1139 dirfds->push_back(dirfd);
1141 bool res =
_visit(item_stats, topts, visitor, *dirfds);
1143 if( dirfds->size() != 1 && res ) {
1144 ERR_PRINT(
"dirfd stack error: count %zu", dirfds->size());
1147 while( !dirfds->empty() ) {
1148 ::close(dirfds->back());
1151 if( !user_dirfds ) {
1166 if( !path_stats.
exists() ) {
1172 if( path_stats.
has_fd() ) {
1182 int res = ::unlink( path_stats.
path().c_str() );
1192 if( !path_stats.
is_dir() ) {
1193 ERR_PRINT(
"remove: Error: path is neither file nor dir: %s\n", path_stats.
to_string().c_str());
1203 struct remove_context_t {
1205 std::vector<int> dirfds;
1221 const int dirfd = ctx_ptr->dirfds.back();
1222 const std::string& basename_ = element_stats.
item().
basename();
1226 const int dirfd2 = *( ctx_ptr->dirfds.end() - 2 );
1227 const int res = ::unlinkat( dirfd2, basename_.c_str(), AT_REMOVEDIR );
1236 const int res = ::unlinkat( dirfd, basename_.c_str(), 0 );
1257 if( !source1.is_file() ) {
1258 ERR_PRINT(
"source1_stats is not a file: %s", source1.to_string().c_str());
1261 if( !source2.is_file() ) {
1262 ERR_PRINT(
"source2_stats is not a file: %s", source2.to_string().c_str());
1266 if( source1.size() != source2.size() ) {
1268 jau::fprintf_td(stderr,
"compare: Source files size mismatch, %s != %s\n",
1269 source1.to_string().c_str(), source2.to_string().c_str());
1274 int src1=-1, src2=-1;
1275 int src_flags = O_RDONLY|
O_BINARY|O_NOCTTY;
1276 uint64_t offset = 0;
1281 ERR_PRINT(
"Failed to open source1 %s, errno %d, %s", source1.to_string().c_str());
1286 ERR_PRINT(
"Failed to open source2 %s, errno %d, %s", source2.to_string().c_str());
1289 while ( offset < source1.size()) {
1291 char buffer1[BUFSIZ];
1292 char buffer2[BUFSIZ];
1294 if( ( rc1 = ::read(src1, buffer1,
sizeof(buffer1)) ) > 0 ) {
1295 ssize_t bytes_to_write = rc1;
1296 size_t buffer_offset = 0;
1297 while( 0 <= rc2 && 0 < bytes_to_write ) {
1298 while( ( rc2 = ::read(src2, buffer2+buffer_offset, bytes_to_write) ) < 0 ) {
1299 if ( errno == EAGAIN || errno == EINTR ) {
1305 buffer_offset += rc2;
1306 bytes_to_write -= rc2;
1307 offset += (uint64_t)rc2;
1309 }
else if ( 0 > rc1 && ( errno == EAGAIN || errno == EINTR ) ) {
1313 if ( 0 > rc1 || 0 > rc2 ) {
1315 ERR_PRINT(
"Failed to read source1 bytes @ %s / %s, %s",
1317 source1.to_string().c_str());
1318 }
else if ( 0 > rc2 ) {
1319 ERR_PRINT(
"Failed to read source2 bytes @ %s / %s, %s",
1321 source2.to_string().c_str());
1325 if( 0 != ::memcmp(buffer1, buffer2, rc1) ) {
1327 jau::fprintf_td(stderr,
"compare: Difference within %s bytes @ %s / %s, %s != %s\n",
1329 source1.to_string().c_str(), source2.to_string().c_str());
1337 if( offset < source1.size() ) {
1338 ERR_PRINT(
"Incomplete transfer %s / %s, %s != %s\n",
1340 source1.to_string().c_str(), source2.to_string().c_str());
1362 const int dst_dirfd,
const std::string& dst_basename,
const copy_options copts)
noexcept {
1363 file_stats dst_stats(dst_dirfd, dst_basename);
1369 jau::fprintf_td(stderr,
"copy: Error: dest_path exists but copy_options::overwrite not set: source %s, dest '%s', copts %s\n",
1370 src_stats.to_string().c_str(), dst_stats.
to_string().c_str(),
to_string( copts ).c_str());
1374 const int res = ::unlinkat(dst_dirfd, dst_basename.c_str(), 0);
1376 ERR_PRINT(
"remove existing dest_path for symbolic-link failed: source %s, dest '%s'",
1377 src_stats.to_string().c_str(), dst_stats.
to_string().c_str());
1384 const std::shared_ptr<std::string>& link_target_path = src_stats.link_target_path();
1385 if(
nullptr == link_target_path || 0 == link_target_path->size() ) {
1386 ERR_PRINT(
"Symbolic link-path is empty %s", src_stats.to_string().c_str());
1390 const int res = ::symlinkat(link_target_path->c_str(), dst_dirfd, dst_basename.c_str());
1394 jau::fprintf_td(stderr,
"copy: Ignored: Failed to create symink %s -> %s, %s, errno %d, %s\n",
1395 dst_basename.c_str(), link_target_path->c_str(), src_stats.to_string().c_str(), errno, ::strerror(errno));
1399 ERR_PRINT(
"Creating symlink failed %s -> %s, %s", dst_basename.c_str(), link_target_path->c_str(), src_stats.to_string().c_str());
1404 struct timespec ts2[2] = { src_stats.atime().to_timespec(), src_stats.mtime().to_timespec() };
1405 if( 0 != ::utimensat(dst_dirfd, dst_basename.c_str(), ts2, AT_SYMLINK_NOFOLLOW) ) {
1406 ERR_PRINT(
"Couldn't preserve time of symlink, source %s, dest '%s'", src_stats.to_string().c_str(), dst_basename.c_str());
1410 const uid_t caller_uid = ::geteuid();
1411 const ::uid_t source_uid = 0 == caller_uid ? src_stats.uid() : -1;
1412 if( 0 != ::fchownat(dst_dirfd, dst_basename.c_str(), source_uid, src_stats.gid(), AT_SYMLINK_NOFOLLOW) ) {
1413 if( errno != EPERM && errno != EINVAL ) {
1414 ERR_PRINT(
"Couldn't preserve ownership of symlink, source %s, dest '%s'", src_stats.to_string().c_str(), dst_basename.c_str());
1419 jau::fprintf_td(stderr,
"copy: Warn: Couldn't preserve ownership of symlink, source %s, dest '%s', errno %d (%s)\n",
1420 src_stats.to_string().c_str(), dst_basename.c_str(), errno, ::strerror(errno));
1431 const uid_t caller_uid = ::geteuid();
1433 int src_flags = O_RDONLY|
O_BINARY|O_NOCTTY;
1434 uint64_t offset = 0;
1437#if defined(__linux__)
1438 if( caller_uid == target_stats->
uid() ) {
1439 src_flags |= O_NOATIME;
1442 src =
__posix_openat64(src_dirfd, src_stats.item().basename().c_str(), src_flags);
1444 if( src_stats.is_link() ) {
1448 ERR_PRINT(
"Failed to open source %s", src_stats.to_string().c_str());
1450 jau::fprintf_td(stderr,
"copy: Ignored: Failed to open source %s, errno %d, %s\n", src_stats.to_string().c_str(), errno, ::strerror(errno));
1456 ERR_PRINT(
"Failed to open target_path '%s'", dst_basename.c_str());
1459 while ( offset < src_stats.size()) {
1461#ifdef _USE_SENDFILE_
1462 off64_t offset_i = (off64_t)offset;
1463 const uint64_t count = std::max<uint64_t>(std::numeric_limits<ssize_t>::max(), src_stats.size() - offset);
1464 if( ( rc1 = ::sendfile64(dst, src, &offset_i, (
size_t)count) ) >= 0 ) {
1465 offset = (uint64_t)offset_i;
1468 char buffer[BUFSIZ];
1469 if( ( rc1 = ::read(src, buffer,
sizeof(buffer)) ) > 0 ) {
1470 ssize_t bytes_to_write = rc1;
1471 size_t buffer_offset = 0;
1472 while( 0 <= rc2 && 0 < bytes_to_write ) {
1473 while( ( rc2 =
::write(dst, buffer+buffer_offset, bytes_to_write) ) < 0 ) {
1474 if ( errno == EAGAIN || errno == EINTR ) {
1480 buffer_offset += rc2;
1481 bytes_to_write -= rc2;
1484 }
else if ( 0 > rc1 && ( errno == EAGAIN || errno == EINTR ) ) {
1489 if ( 0 > rc1 || 0 > rc2 ) {
1490#ifdef _USE_SENDFILE_
1491 ERR_PRINT(
"Failed to copy bytes @ %s / %s, %s -> '%s'",
1493 src_stats.to_string().c_str(), dst_basename.c_str());
1496 ERR_PRINT(
"Failed to read bytes @ %s / %s, %s",
1498 src_stats.to_string().c_str());
1499 }
else if ( 0 > rc2 ) {
1500 ERR_PRINT(
"Failed to write bytes @ %s / %s, %s",
1502 dst_basename.c_str());
1511 if( offset < src_stats.size() ) {
1512 ERR_PRINT(
"Incomplete transfer %s / %s, %s -> '%s'",
1514 src_stats.to_string().c_str(), dst_basename.c_str());
1521 ERR_PRINT(
"Couldn't restore omitted permissions, source %s, dest '%s'",
1522 src_stats.to_string().c_str(), dst_basename.c_str());
1529 if( 0 != ::futimens(dst, ts2) ) {
1530 ERR_PRINT(
"Couldn't preserve time of file, source %s, dest '%s'",
1531 src_stats.to_string().c_str(), dst_basename.c_str());
1535 ::uid_t source_uid = 0 == caller_uid ? target_stats->
uid() : -1;
1536 if( 0 != ::fchown(dst, source_uid, target_stats->
gid()) ) {
1537 if( errno != EPERM && errno != EINVAL ) {
1538 ERR_PRINT(
"Couldn't preserve ownership of file, uid(caller %" PRIu32
", chown %" PRIu32
"), source %s, dest '%s'",
1539 caller_uid, source_uid, src_stats.to_string().c_str(), dst_basename.c_str());
1544 jau::fprintf_td(stderr,
"copy: Ignored: Preserve ownership of file failed, uid(caller %" PRIu32
", chown %" PRIu32
"), source %s, dest '%s', errno %d (%s)\n",
1545 caller_uid, source_uid, src_stats.to_string().c_str(), dst_stats.
to_string().c_str(), errno, ::strerror(errno));
1551 if( 0 != ::fsync(dst) ) {
1552 ERR_PRINT(
"Couldn't synchronize destination file, source %s, dest '%s'",
1553 src_stats.to_string().c_str(), dst_basename.c_str());
1571 bool new_dir =
false;
1572 std::string basename_;
1573 const int dest_dirfd = ctx.dst_dirfds.back();
1574 if( dst_stats.is_dir() ) {
1576 jau::fprintf_td(stderr,
"copy: mkdir directory already exist: %s\n", dst_stats.to_string().c_str());
1578 basename_.append( dst_stats.item().basename() );
1579 }
else if( !dst_stats.exists() ) {
1581 constexpr const int32_t val_min = 888;
1582 constexpr const int32_t val_max = std::numeric_limits<int32_t>::max();
1583 uint64_t mkdir_cntr = 0;
1584 std::mt19937_64 prng;
1585 std::uniform_int_distribution<int32_t> prng_dist(val_min, val_max);
1586 bool mkdir_ok =
false;
1589 const int32_t val_d = prng_dist(prng);
1594 }
else if (errno != EINTR && errno != EEXIST) {
1595 ERR_PRINT(
"mkdir failed: %s, temp '%s'", dst_stats.to_string().c_str(), basename_.c_str());
1598 }
while( !mkdir_ok && mkdir_cntr < val_max );
1600 ERR_PRINT(
"mkdir failed: %s", dst_stats.to_string().c_str());
1604 ERR_PRINT(
"mkdir failed: %s, exists but is no dir", dst_stats.to_string().c_str());
1609 if ( 0 > new_dirfd ) {
1611 ERR_PRINT(
"Couldn't open new dir %s, temp '%s'", dst_stats.to_string().c_str(), basename_.c_str());
1613 ERR_PRINT(
"Couldn't open new dir %s", dst_stats.to_string().c_str());
1616 ::unlinkat(dest_dirfd, basename_.c_str(), AT_REMOVEDIR);
1623 ::unlinkat(dest_dirfd, basename_.c_str(), AT_REMOVEDIR);
1624 ERR_PRINT(
"zero permissions on dest %s, temp '%s'", dst_stats.to_string().c_str(), basename_.c_str());
1626 ERR_PRINT(
"zero permissions on dest %s", dst_stats.to_string().c_str());
1632#if defined(__linux__) && defined(__GLIBC__)
1633 const int rename_flags = 0;
1634 const int rename_res = ::renameat2(dest_dirfd, basename_.c_str(), dest_dirfd, dst_stats.item().basename().c_str(), rename_flags);
1636 const int rename_res = ::renameat(dest_dirfd, basename_.c_str(), dest_dirfd, dst_stats.item().basename().c_str());
1638 if( 0 != rename_res ) {
1639 ERR_PRINT(
"rename temp to dest, temp '%s', dest %s", basename_.c_str(), dst_stats.to_string().c_str());
1640 ::unlinkat(dest_dirfd, basename_.c_str(), AT_REMOVEDIR);
1645 ctx.dst_dirfds.push_back(new_dirfd);
1650 const file_stats* target_stats = src_stats.
is_link() ? src_stats.link_target().get() : &src_stats;
1655 ERR_PRINT(
"restore permissions, source %s, dest '%s'", src_stats.to_string().c_str(), dst_basename.c_str());
1662 if( 0 != ::futimens(dst_dirfd, ts2) ) {
1663 ERR_PRINT(
"preserve time of file failed, source %s, dest '%s'", src_stats.to_string().c_str(), dst_basename.c_str());
1667 const uid_t caller_uid = ::geteuid();
1668 const ::uid_t source_uid = 0 == caller_uid ? target_stats->
uid() : -1;
1669 if( 0 != ::fchown(dst_dirfd, source_uid, target_stats->
gid()) ) {
1670 if( errno != EPERM && errno != EINVAL ) {
1671 ERR_PRINT(
"dir_preserve ownership of file failed, uid(caller %" PRIu32
", chown %" PRIu32
"), source %s, dest '%s'",
1672 caller_uid, source_uid, src_stats.to_string().c_str(), dst_basename.c_str());
1677 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",
1678 caller_uid, source_uid, src_stats.to_string().c_str(), dst_basename.c_str(), errno, ::strerror(errno));
1683 if( 0 != ::fsync(dst_dirfd) ) {
1684 ERR_PRINT(
"Couldn't synchronize destination file '%s'", dst_basename.c_str());
1705 if( source_stats.
is_file() ) {
1709 if( target_stats.
exists() ) {
1710 if( target_stats.
is_file() ) {
1713 jau::fprintf_td(stderr,
"copy: Error: source_path is file, target_path existing file w/o overwrite, source %s, target %s\n",
1721 if ( 0 > src_dirfd ) {
1722 ERR_PRINT(
"source_path dir couldn't be opened, source %s", source_stats.
to_string().c_str());
1726 std::string dst_basename;
1728 if( target_stats.
is_dir() ) {
1731 if ( 0 > dst_dirfd ) {
1732 ERR_PRINT(
"target dir couldn't be opened, target %s", target_stats.
to_string().c_str());
1740 if( !target_parent_stats.
is_dir() ) {
1742 jau::fprintf_td(stderr,
"copy: Error: target parent is not an existing directory, target %s, target_parent %s\n",
1749 if ( 0 > dst_dirfd ) {
1750 ERR_PRINT(
"target_parent dir couldn't be opened, target %s, target_parent %s",
1757 if( !
copy_file(src_dirfd, source_stats, dst_dirfd, dst_basename, copts) ) {
1764 if( !source_stats.
is_dir() ) {
1766 jau::fprintf_td(stderr,
"copy: Error: source_path is neither file nor dir, source %s, target %s\n",
1775 copy_context_t ctx { copts, 0, std::vector<int>(), std::vector<int>() };
1783 if( target_stats.
exists() && !target_stats.
is_dir() ) {
1785 jau::fprintf_td(stderr,
"copy: Error: source_path is dir but target_path exist and is no dir, source %s, target %s\n",
1795 if ( 0 > dst_dirfd ) {
1796 ERR_PRINT(
"target dir couldn't be opened, target %s", target_stats.
to_string().c_str());
1799 ctx.dst_dirfds.push_back(dst_dirfd);
1804 if( !target_parent_stats.
is_dir() ) {
1806 jau::fprintf_td(stderr,
"copy: Error: target parent is not an existing directory, target %s, target_parent %s\n",
1812 if ( 0 > dst_parent_dirfd ) {
1813 ERR_PRINT(
"target dirname couldn't be opened, target %s, target_parent %s",
1817 ctx.dst_dirfds.push_back(dst_parent_dirfd);
1819 if( target_stats.
is_dir() ) {
1822 if ( 0 > dst_dirfd ) {
1823 ERR_PRINT(
"target dir couldn't be opened, target %s", target_stats.
to_string().c_str());
1826 ctx.dst_dirfds.push_back(dst_dirfd);
1832 ctx.skip_dst_dir_mkdir = 1;
1845 ERR_PRINT(
"dirfd stack error: count[src %zu, dst %zu, dst_skip %d] @ %s",
1849 const int src_dirfd = ctx_ptr->
src_dirfds.back();
1850 const int dst_dirfd = ctx_ptr->
dst_dirfds.back();
1851 const std::string& basename_ = element_stats.
item().
basename();
1856 const file_stats target_stats_(dst_dirfd, basename_);
1863 ERR_PRINT(
"dirfd stack error: count[src %zu, dst %zu] @ %s",
1873 if( !
copy_file(src_dirfd, element_stats, dst_dirfd, basename_, ctx_ptr->
copts) ) {
1879 bool res =
jau::fs::visit(source_stats, topts, pv, &ctx.src_dirfds);
1880 while( !ctx.dst_dirfds.empty() ) {
1881 ::close(ctx.dst_dirfds.back());
1882 ctx.dst_dirfds.pop_back();
1891 ERR_PRINT(
"oldpath doesn't exist, oldpath %s, newpath %s\n",
1895 if( 0 !=
::rename(oldpath_stats.
path().c_str(), newpath_stats.
path().c_str()) ) {
1896 ERR_PRINT(
"rename failed, oldpath %s, newpath %s\n",
1908 if( 0 != ::seteuid(user_id) ) {
1909 ERR_PRINT(
"seteuid(%" PRIu32
") failed", user_id);
1916 const mountflags_t flags,
const std::string& fs_options)
1924 if( !target_stats.
is_dir()) {
1928 const std::string target_path(target_stats.
path());
1930 if( 0 > backingfile ) {
1931 ERR_PRINT(
"Couldn't open image-file '%s': res %d", image_stats.
to_string().c_str(), backingfile);
1934#if defined(__linux__)
1935 const ::uid_t caller_uid = ::geteuid();
1936 int loop_device_id = -1;
1938 ::pid_t pid = ::fork();
1940 int loop_ctl_fd = -1, loop_device_fd = -1;
1941 char loopname[4096];
1943 void* fs_options_cstr =
nullptr;
1945 if( 0 != caller_uid ) {
1951 if( 0 > loop_ctl_fd ) {
1952 ERR_PRINT(
"Couldn't open loop-control: res %d", loop_ctl_fd);
1956 loop_device_id = ::ioctl(loop_ctl_fd, LOOP_CTL_GET_FREE);
1957 if( 0 > loop_device_id ) {
1958 ERR_PRINT(
"Couldn't get free loop-device: res %d", loop_device_id);
1961 if( 254 < loop_device_id ) {
1962 ERR_PRINT(
"loop-device %d out of valid range [0..254]", loop_device_id);
1965 ::close(loop_ctl_fd);
1968 snprintf(loopname,
sizeof(loopname),
"/dev/loop%d", loop_device_id);
1972 if( 0 > loop_device_fd ) {
1973 ERR_PRINT(
"Couldn't open loop-device '%s': res %d", loopname, loop_device_fd);
1976 if( 0 > ::ioctl(loop_device_fd, LOOP_SET_FD, backingfile) ) {
1977 ERR_PRINT(
"Couldn't attach image-file '%s' to loop-device '%s'", image_stats.
to_string().c_str(), loopname);
1981 if( fs_options.size() > 0 ) {
1982 fs_options_cstr = (
void*) fs_options.data();
1984 mount_res =
::mount(loopname, target_path.c_str(), fs_type.c_str(), flags, fs_options_cstr);
1985 if( 0 != mount_res ) {
1986 ERR_PRINT(
"source_path %s, target_path %s, fs_type %s, res %d",
1987 image_stats.
path().c_str(), target_path.c_str(), fs_type.c_str(), mount_res);
1988 ::ioctl(loop_device_fd, LOOP_CLR_FD, 0);
1991 ::close(loop_device_fd);
1992 ::_exit(loop_device_id+1);
1995 if( 0 <= loop_ctl_fd ) {
1996 ::close(loop_ctl_fd);
1998 if( 0 <= loop_device_fd ) {
1999 ::close(loop_device_fd);
2002 }
else if( 0 < pid ) {
2004 ::pid_t child_pid = ::waitpid(pid, &pid_status, 0);
2005 if( 0 > child_pid ) {
2006 ERR_PRINT(
"wait(%d) failed: child_pid %d", pid, child_pid);
2008 if( child_pid != pid ) {
2009 WARN_PRINT(
"wait(%d) terminated child_pid %d", pid, child_pid);
2011 if( !WIFEXITED(pid_status) ) {
2012 WARN_PRINT(
"wait(%d) terminated abnormally child_pid %d, pid_status %d", pid, child_pid, pid_status);
2015 loop_device_id = WEXITSTATUS(pid_status);
2016 if( 0 >= loop_device_id ) {
2022 ERR_PRINT(
"Couldn't fork() process: res %d", pid);
2025 if( 0 <= backingfile ) {
2026 ::close(backingfile);
2028 return mount_ctx(target_path, loop_device_id);
2036 if( 0 <= backingfile ) {
2037 ::close(backingfile);
2043 const mountflags_t flags,
const std::string& fs_options)
2045 if( source.empty() ) {
2046 ERR_PRINT(
"source is an empty string ");
2051 if( !target_stats.
is_dir()) {
2055 const std::string target_path(target_stats.
path());
2056 const ::uid_t caller_uid = ::geteuid();
2058 ::pid_t pid = ::fork();
2060 void* fs_options_cstr =
nullptr;
2062 if( 0 != caller_uid ) {
2064 ::_exit( EXIT_FAILURE );
2067 if( fs_options.size() > 0 ) {
2068 fs_options_cstr = (
void*) fs_options.data();
2070#if defined(__linux__)
2071 const int mount_res =
::mount(source_stats.
path().c_str(), target_path.c_str(), fs_type.c_str(), flags, fs_options_cstr);
2072#elif defined(__FreeBSD__)
2079 (void)fs_options_cstr;
2080 const int mount_res = -1;
2082 #if !defined(JAU_OS_TYPE_WASM)
2083 #warning Add OS support
2086 (void)fs_options_cstr;
2087 const int mount_res = -1;
2089 if( 0 != mount_res ) {
2090 ERR_PRINT(
"source_path %s, target_path %s, fs_type %s, flags %" PRIu64
", res %d",
2091 source_stats.
path().c_str(), target_path.c_str(), fs_type.c_str(), flags, mount_res);
2092 ::_exit( EXIT_FAILURE );
2094 ::_exit( EXIT_SUCCESS );
2097 }
else if( 0 < pid ) {
2099 ::pid_t child_pid = ::waitpid(pid, &pid_status, 0);
2100 if( 0 > child_pid ) {
2101 ERR_PRINT(
"wait(%d) failed: child_pid %d", pid, child_pid);
2103 if( child_pid != pid ) {
2104 WARN_PRINT(
"wait(%d) terminated child_pid %d", pid, child_pid);
2106 if( !WIFEXITED(pid_status) ) {
2107 WARN_PRINT(
"wait(%d) terminated abnormally child_pid %d, pid_status %d", pid, child_pid, pid_status);
2108 }
else if( EXIT_SUCCESS == WEXITSTATUS(pid_status) ) {
2113 ERR_PRINT(
"Couldn't fork() process: res %d", pid);
2124 if( !target_stats.
is_dir()) {
2127 const ::uid_t caller_uid = ::geteuid();
2129 ::pid_t pid = ::fork();
2131 if( 0 != caller_uid ) {
2133 ::_exit( EXIT_FAILURE );
2136#if defined(__linux__)
2137 const int umount_res = ::umount2(target_stats.
path().c_str(), flags);
2138#elif defined(__FreeBSD__)
2139 const int umount_res = ::unmount(target_stats.
path().c_str(), flags);
2141 #if !defined(JAU_OS_TYPE_WASM)
2142 #warning Add OS support
2144 const int umount_res = -1;
2146 if( 0 != umount_res ) {
2147 ERR_PRINT(
"Couldn't umount '%s', flags %d: res %d\n", target_stats.
to_string().c_str(), flags, umount_res);
2151 ::_exit(0 == umount_res ? EXIT_SUCCESS : EXIT_FAILURE);
2153#if defined(__linux__)
2154 int loop_device_fd = -1;
2155 char loopname[4096];
2157 snprintf(loopname,
sizeof(loopname),
"/dev/loop%d", context.
loop_device_id);
2161 if( 0 > loop_device_fd ) {
2162 ERR_PRINT(
"Couldn't open loop-device '%s': res %d", loopname, loop_device_fd);
2165 if( 0 > ::ioctl(loop_device_fd, LOOP_CLR_FD, 0) ) {
2166 ERR_PRINT(
"Couldn't detach loop-device '%s'", loopname);
2169 ::close(loop_device_fd);
2170 ::_exit(0 == umount_res ? EXIT_SUCCESS : EXIT_FAILURE);
2174 ::_exit( EXIT_FAILURE );
2176#if defined(__linux__)
2178 if( 0 <= loop_device_fd ) {
2179 ::close(loop_device_fd);
2181 ::_exit( EXIT_FAILURE );
2183 }
else if( 0 < pid ) {
2185 ::pid_t child_pid = ::waitpid(pid, &pid_status, 0);
2186 if( 0 > child_pid ) {
2187 ERR_PRINT(
"wait(%d) failed: child_pid %d", pid, child_pid);
2189 if( child_pid != pid ) {
2190 WARN_PRINT(
"wait(%d) terminated child_pid %d", pid, child_pid);
2192 if( !WIFEXITED(pid_status) ) {
2193 WARN_PRINT(
"wait(%d) terminated abnormally child_pid %d, pid_status %d", pid, child_pid, pid_status);
2194 }
else if( EXIT_SUCCESS == WEXITSTATUS(pid_status) ) {
2199 ERR_PRINT(
"Couldn't fork() process: res %d", pid);
2206 if( target.empty() ) {
2210 if( !target_stats.
is_dir()) {
2213 const ::uid_t caller_uid = ::geteuid();
2215 ::pid_t pid = ::fork();
2217 if( 0 != caller_uid ) {
2219 ::_exit( EXIT_FAILURE );
2222#if defined(__linux__)
2223 const int umount_res = ::umount2(target_stats.
path().c_str(), flags);
2224#elif defined(__FreeBSD__)
2225 const int umount_res = ::unmount(target_stats.
path().c_str(), flags);
2227 #if !defined(JAU_OS_TYPE_WASM)
2228 #warning Add OS support
2230 const int umount_res = -1;
2232 if( 0 == umount_res ) {
2233 ::_exit( EXIT_SUCCESS );
2235 ERR_PRINT(
"Couldn't umount '%s', flags %d: res %d\n", target_stats.
to_string().c_str(), flags, umount_res);
2236 ::_exit( EXIT_FAILURE );
2238 }
else if( 0 < pid ) {
2240 ::pid_t child_pid = ::waitpid(pid, &pid_status, 0);
2241 if( 0 > child_pid ) {
2242 ERR_PRINT(
"wait(%d) failed: child_pid %d", pid, child_pid);
2244 if( child_pid != pid ) {
2245 WARN_PRINT(
"wait(%d) terminated child_pid %d", pid, child_pid);
2247 if( !WIFEXITED(pid_status) ) {
2248 WARN_PRINT(
"wait(%d) terminated abnormally child_pid %d, pid_status %d", pid, child_pid, pid_status);
2249 }
else if( EXIT_SUCCESS == WEXITSTATUS(pid_status) ) {
2254 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...