10 if (e.
first(ptr))
do {
13 ptr->open(p_out,p,p_abort);
15 }
catch(exception_io_data
const &) {}
17 throw exception_io_data();
21 try {
seek(p_position, p_abort); }
catch(exception_io_seek_out_of_range) {
throw exception_io_data();}
26 case seek_from_beginning:
27 seek(p_position,p_abort);
29 case seek_from_current:
30 seek(p_position + get_position(p_abort),p_abort);
33 seek(p_position + get_size_ex(p_abort),p_abort);
36 throw exception_io_data();
45 }
catch(std::bad_alloc) {
46 if (size < 256)
throw;
60 delta = p_src->read(ptr,delta,p_abort);
62 p_dst->write(ptr,delta,p_abort);
69 if (g_transfer(p_src,p_dst,p_bytes,p_abort) != p_bytes)
70 throw exception_io_data_truncation();
76 TRACK_CALL_TEXT(
"filesystem::g_get_canonical_path");
80 if (e.
first(ptr))
do {
81 if (ptr->get_canonical_path(path,out))
return;
89 TRACK_CALL_TEXT(
"filesystem::g_get_display_path");
91 if (!g_get_interface(ptr,path))
98 if (!ptr->get_display_path(path,out))
112 return strstr( path,
"://" ) == NULL;
115 filesystem::ptr filesystem::g_get_interface(
const char * path) {
117 if (!g_get_interface(rv, path))
throw exception_io_no_handler_for_path();
125 if (e.
first(ptr))
do {
126 if (ptr->is_our_path(path))
131 }
while(e.
next(ptr));
138 TRACK_CALL_TEXT(
"filesystem::g_open");
139 g_get_interface(path)->open(p_out,path,mode,p_abort);
144 FB2K_RETRY_ON_SHARING_VIOLATION(
g_open(p_out, p_path, p_mode, p_abort), p_abort, p_timeout);
152 g_get_stats(p_path,stats,dummy,p_abort);
153 }
catch(exception_io_not_found) {
return false;}
157 bool filesystem::g_exists_writeable(
const char * p_path,
abort_callback & p_abort)
162 g_get_stats(p_path,stats,writeable,p_abort);
163 }
catch(exception_io_not_found) {
return false;}
168 g_get_interface(p_path)->remove(p_path,p_abort);
171 void filesystem::g_remove_timeout(
const char * p_path,
double p_timeout,
abort_callback & p_abort) {
172 FB2K_RETRY_FILE_MOVE( g_remove(p_path, p_abort), p_abort, p_timeout );
175 void filesystem::g_move_timeout(
const char * p_src,
const char * p_dst,
double p_timeout,
abort_callback & p_abort) {
176 FB2K_RETRY_FILE_MOVE( g_move(p_src, p_dst, p_abort), p_abort, p_timeout );
179 void filesystem::g_copy_timeout(
const char * p_src,
const char * p_dst,
double p_timeout,
abort_callback & p_abort) {
180 FB2K_RETRY_FILE_MOVE( g_copy(p_src, p_dst, p_abort), p_abort, p_timeout );
183 void filesystem::g_create_directory(
const char * p_path,
abort_callback & p_abort)
185 g_get_interface(p_path)->create_directory(p_path,p_abort);
188 void filesystem::g_move(
const char * src,
const char * dst,
abort_callback & p_abort) {
191 if (e.
first(ptr))
do {
192 if (ptr->is_our_path(src) && ptr->is_our_path(dst)) {
193 ptr->move(src,dst,p_abort);
196 }
while(e.
next(ptr));
197 throw exception_io_no_handler_for_path();
200 void filesystem::g_link(
const char * p_src,
const char * p_dst,
abort_callback & p_abort) {
205 void filesystem::g_link_timeout(
const char * p_src,
const char * p_dst,
double p_timeout,
abort_callback & p_abort) {
206 FB2K_RETRY_FILE_MOVE( g_link(p_src, p_dst, p_abort), p_abort, p_timeout );
212 TRACK_CALL_TEXT(
"filesystem::g_list_directory");
213 g_get_interface(p_path)->list_directory(p_path,p_out,p_abort);
220 out << (unsigned) strlen(src);
229 if (src[ptr++]!=
'|')
return -1;
230 int len = atoi(src+ptr);
231 if (len<=0)
return -1;
232 while(src[ptr]!=0 && src[ptr]!=
'|') ptr++;
233 if (src[ptr]!=
'|')
return -1;
238 if (src[ptr]==0)
return -1;
241 if (src[ptr]!=
'|')
return -1;
250 if (fs->is_remote(p_path))
throw exception_io_object_is_remote();
251 fs->open(p_out,p_path,open_mode_read,p_abort);
254 bool filesystem::g_is_remote(
const char * p_path) {
255 return g_get_interface(p_path)->is_remote(p_path);
258 bool filesystem::g_is_recognized_and_remote(
const char * p_path) {
260 if (g_get_interface(fs,p_path))
return fs->is_remote(p_path);
264 bool filesystem::g_is_remote_or_unrecognized(
const char * p_path) {
266 if (g_get_interface(fs,p_path))
return fs->is_remote(p_path);
270 bool filesystem::g_relative_path_create(
const char * file_path,
const char * playlist_path,
pfc::string_base & out)
276 if (g_get_interface(fs,file_path))
277 rv = fs->relative_path_create(file_path,playlist_path,out);
282 bool filesystem::g_relative_path_parse(
const char * relative_path,
const char * playlist_path,
pfc::string_base & out)
286 if (e.
first(ptr))
do {
287 if (ptr->relative_path_parse(relative_path,playlist_path,out))
return true;
288 }
while(e.
next(ptr));
296 if (is_our_path(path))
299 if (g_parse_unpack_path(path,archive,file))
301 g_get_canonical_path(archive,archive_canonical);
302 make_unpack_path(out,archive_canonical,file);
311 bool archive_impl::is_our_path(
const char * path)
313 if (!g_is_unpack_path(path))
return false;
314 const char * type = get_archive_type();
318 if (*type!=*path)
return false;
322 if (*path!=
'|')
return false;
329 if (g_parse_unpack_path(path,archive,file))
331 g_get_display_path(archive,out);
341 if (mode != open_mode_read)
throw exception_io_denied();
343 if (!g_parse_unpack_path(path,archive,file))
throw exception_io_not_found();
344 open_archive(p_out,archive,file,p_abort);
349 throw exception_io_denied();
352 void archive_impl::move(
const char * src,
const char * dst,
abort_callback & p_abort) {
353 throw exception_io_denied();
356 bool archive_impl::is_remote(
const char * src) {
358 if (g_parse_unpack_path(src,archive,file))
return g_is_remote(archive);
359 else throw exception_io_not_found();
362 bool archive_impl::relative_path_create(
const char * file_path,
const char * playlist_path,
pfc::string_base & out) {
364 if (g_parse_unpack_path(file_path,archive,file))
367 if (g_relative_path_create(archive,playlist_path,archive_rel))
370 make_unpack_path(out_path,archive_rel,file);
378 bool archive_impl::relative_path_parse(
const char * relative_path,
const char * playlist_path,
pfc::string_base & out)
380 if (!is_our_path(relative_path))
return false;
382 if (g_parse_unpack_path(relative_path,archive_rel,file))
385 if (g_relative_path_parse(archive_rel,playlist_path,archive))
388 make_unpack_path(out_path,archive,file);
397 PFC_ASSERT( g_is_unpack_path(path) );
399 const char * split = strchr(path,
'|');
400 if (base == NULL || split == NULL || base > split)
return false;
404 if (delta<0)
return false;
410 PFC_ASSERT( g_is_unpack_path(path) );
411 path = strchr(path,
'|');
412 if (!path)
return false;
414 if (delta<0)
return false;
420 bool archive_impl::g_is_unpack_path(
const char * path) {
432 void archive_impl::make_unpack_path(
pfc::string_base & path,
const char *
archive,
const char *
file) {g_make_unpack_path(path,archive,file,get_archive_type());}
435 FILE * filesystem::streamio_open(
const char * path,
const char * flags)
439 g_get_canonical_path(path,temp);
440 if (!strncmp(temp,
"file://",7))
454 directory_callback_isempty() : m_isempty(
true) {}
460 bool isempty() {
return m_isempty;}
471 bool filesystem::g_is_empty_directory(
const char * path,
abort_callback & p_abort)
473 directory_callback_isempty callback;
475 g_list_directory(path,callback,p_abort);
476 }
catch(exception_io
const &) {
return false;}
477 return callback.isempty();
480 bool filesystem::g_is_valid_directory(
const char * path,
abort_callback & p_abort) {
482 directory_callback_dummy cb;
483 g_list_directory(path,cb,p_abort);
485 }
catch(exception_io
const &) {
return false;}
490 if (is_subdirectory) {
494 }
catch(exception_io
const &) {}
497 m_data.add_item(pfc::rcnew_t<t_entry>(url,p_stats));
506 directory_callback_impl_copy(
const char * p_target, filesystem::ptr fs) : m_fs(fs)
509 m_target.fix_dir_separator(
'\\');
514 t_size truncat = m_target.length();
516 if (is_subdirectory) {
518 m_fs->create_directory(m_target,p_abort);
519 }
catch(exception_io_already_exists) {}
523 _copy(url, m_target, owner, p_abort);
525 m_target.truncate(truncat);
532 srcFS->
open(r_src,src,filesystem::open_mode_read,p_abort);
533 size = r_src->get_size_ex(p_abort);
534 m_fs->open(r_dst,dst,filesystem::open_mode_write_new,p_abort);
538 file::g_transfer_object(r_src,r_dst,size,p_abort);
542 try {m_fs->remove(dst,dummy);}
catch(...) {}
549 filesystem::ptr m_fs;
553 void filesystem::copy_directory(
const char * src,
const char * dst,
abort_callback & p_abort) {
555 this->create_directory( dst, p_abort );
556 }
catch(exception_io_already_exists) {}
557 directory_callback_impl_copy cb(dst,
this);
558 list_directory(src, cb, p_abort);
561 void filesystem::g_copy_directory(
const char * src,
const char * dst,
abort_callback & p_abort) {
562 filesystem::ptr dstFS = filesystem::g_get_interface(dst);
564 dstFS->create_directory( dst, p_abort );
565 }
catch(exception_io_already_exists) {}
566 directory_callback_impl_copy cb(dst, dstFS);
567 g_list_directory(src,cb,p_abort);
570 void filesystem::g_copy(
const char * src,
const char * dst,
abort_callback & p_abort) {
574 g_open(r_src,src,open_mode_read,p_abort);
575 size = r_src->get_size_ex(p_abort);
576 g_open(r_dst,dst,open_mode_write_new,p_abort);
580 file::g_transfer_object(r_src,r_dst,size,p_abort);
584 try {g_remove(dst,dummy);}
catch(...) {}
591 if (
read(p_buffer,p_bytes,p_abort) != p_bytes)
throw exception_io_data_truncation();
608 delta =
sizeof(temp);
609 if (delta > todo) delta = (
t_size) todo;
610 deltadone =
read(temp,delta,p_abort);
613 if (deltadone < delta)
break;
619 if (skip(p_bytes,p_abort) != p_bytes)
throw exception_io_data_truncation();
623 g_open(p_out,p_path,open_mode_write_new,p_abort);
626 t_filesize length = p_from->get_size(p_abort);
627 p_from->reopen( p_abort );
629 p_to->seek(0,p_abort);
630 p_to->set_eof(p_abort);
632 g_transfer(p_from, p_to, ~0, p_abort);
633 }
else if (length > 0) {
634 g_transfer_object(p_from,p_to,length,p_abort);
639 g_open(p_out,
"tempfile://",open_mode_write_new,p_abort);
643 g_open(p_out,
"tempmem://",open_mode_write_new,p_abort);
646 file::ptr filesystem::g_open_tempmem() {
648 file::ptr f; g_open_tempmem(f, aborter);
return f;
652 throw exception_io_not_found();
656 throw exception_io_denied();
660 TRACK_CALL_TEXT(
"filesystem::g_get_stats");
661 return g_get_interface(p_path)->get_stats(p_path,p_stats,p_is_writeable,p_abort);
666 if (g_parse_unpack_path(p_path,archive,file)) {
667 if (g_is_remote(archive))
throw exception_io_object_is_remote();
668 p_is_writeable =
false;
669 p_stats = get_stats_in_archive(archive,file,p_abort);
671 else throw exception_io_not_found();
677 position = get_position(p_abort);
680 return position >= size;
690 const t_size expBase = 64*1024;
691 if (p_bytes > expBase) {
693 t_size allocWalk = expBase;
698 read_object(temp.
get_ptr() + done, target - done, p_abort);
699 if (target == p_bytes)
break;
706 read_object(buf.
get_ptr(),p_bytes,p_abort);
712 read_lendian_t(length,p_abort);
713 read_string_ex(p_out,length,p_abort);
722 delta_done =
read(buffer,delta,p_abort);
724 if (delta_done < delta)
break;
729 write_lendian_t(len,p_abort);
730 write_object(p_string,len,p_abort);
733 void stream_writer::write_string(
const char * p_string,
abort_callback & p_abort) {
734 write_string(p_string,~0,p_abort);
737 void stream_writer::write_string_raw(
const char * p_string,
abort_callback & p_abort) {
738 write_object(p_string,strlen(p_string),p_abort);
742 if (p_position <
get_size(p_abort)) resize(p_position,p_abort);
755 class exception_io_win32_ex :
public exception_io_win32 {
758 exception_io_win32_ex(
const exception_io_win32_ex & p_other) {*
this = p_other;}
759 const char * what()
const throw() {
return m_msg;}
766 if (p_code == ERROR_ACCESS_DENIED) {
768 if (attr != ~0 && (attr & FILE_ATTRIBUTE_READONLY) != 0)
throw exception_io_denied_readonly();
777 case ERROR_ALREADY_EXISTS:
778 case ERROR_FILE_EXISTS:
779 throw exception_io_already_exists();
780 case ERROR_NETWORK_ACCESS_DENIED:
781 case ERROR_ACCESS_DENIED:
782 throw exception_io_denied();
783 case ERROR_WRITE_PROTECT:
784 throw exception_io_write_protected();
786 case ERROR_PATH_BUSY:
787 case ERROR_SHARING_VIOLATION:
788 case ERROR_LOCK_VIOLATION:
789 throw exception_io_sharing_violation();
790 case ERROR_HANDLE_DISK_FULL:
791 case ERROR_DISK_FULL:
792 throw exception_io_device_full();
793 case ERROR_FILE_NOT_FOUND:
794 case ERROR_PATH_NOT_FOUND:
795 throw exception_io_not_found();
796 case ERROR_BROKEN_PIPE:
798 throw exception_io_no_data();
799 case ERROR_NETWORK_UNREACHABLE:
800 case ERROR_NETNAME_DELETED:
801 throw exception_io_network_not_reachable();
802 case ERROR_NOT_READY:
803 throw exception_io_device_not_ready();
804 case ERROR_INVALID_DRIVE:
805 throw exception_io_invalid_drive();
807 case ERROR_FILE_CORRUPT:
808 case ERROR_DISK_CORRUPT:
809 throw exception_io_file_corrupted();
810 case ERROR_BUFFER_OVERFLOW:
811 throw exception_io_buffer_overflow();
812 case ERROR_DISK_CHANGE:
813 throw exception_io_disk_change();
814 case ERROR_DIR_NOT_EMPTY:
815 throw exception_io_directory_not_empty();
816 case ERROR_INVALID_NAME:
817 throw exception_io_invalid_path_syntax();
818 case ERROR_NO_SYSTEM_RESOURCES:
819 case ERROR_NONPAGED_SYSTEM_RESOURCES:
820 case ERROR_PAGED_SYSTEM_RESOURCES:
821 case ERROR_WORKING_SET_QUOTA:
822 case ERROR_PAGEFILE_QUOTA:
823 case ERROR_COMMITMENT_LIMIT:
824 throw exception_io(
"Insufficient system resources");
825 case ERROR_IO_DEVICE:
826 throw exception_io(
"Device error");
828 throw exception_io_win32_ex(p_code);
839 void file::ensure_local() {
840 if (is_remote())
throw exception_io_object_is_remote();
843 void file::ensure_seekable() {
844 if (!can_seek())
throw exception_io_object_not_seekable();
847 bool filesystem::g_is_recognized_path(
const char * p_path) {
849 return g_get_interface(obj,p_path);
856 return length - position;
862 t_filesize remaining = length - get_position(p_abort);
863 if (remaining < bytes)
throw exception_io_data_truncation();
869 return g_transfer(pfc::implicit_cast<stream_reader*>(p_src.
get_ptr()),pfc::implicit_cast<stream_writer*>(p_dst.
get_ptr()),p_bytes,p_abort);
875 t_filesize srcFileSize = p_src->get_size(p_abort);
876 if (srcFileSize != ~0) {
877 t_filesize remaining = srcFileSize - p_src->get_position(p_abort);
878 if (p_bytes > remaining)
throw exception_io_data_truncation();
881 t_filesize oldsize = p_dst->get_size(p_abort);
883 t_filesize newpos = p_dst->get_position(p_abort) + p_bytes;
884 if (newpos > oldsize) p_dst->resize(newpos ,p_abort);
888 g_transfer_object(pfc::implicit_cast<stream_reader*>(p_src.
get_ptr()),pfc::implicit_cast<stream_writer*>(p_dst.
get_ptr()),p_bytes,p_abort);
897 hasher->initialize(state);
898 hasher->process(state,p_origpath,strlen(p_origpath));
899 hasher->process(state,p_extension,strlen(p_extension));
900 hasher->process(state,p_magic,strlen(p_magic));
901 hash = hasher->get_result(state);
909 p_out += p_extension;
915 const t_filesize position = get_position(p_abort);
917 seek(position + toskip,p_abort);
920 this->seek_ex( p_bytes, seek_from_current, p_abort );
926 if (p_bytes > 1024 && can_seek()) {
929 const t_filesize position = get_position(p_abort);
931 seek(position + toskip,p_abort);
935 return stream_reader::skip(p_bytes,p_abort);
943 static const char header[] =
"file://";
static const t_size headerLen = 7;
944 if (strncmp(p_fspath,header,headerLen) != 0)
return false;
945 p_fspath += headerLen;
956 if (p_fspath[0] !=
'\\' || p_fspath[1] !=
'\\') {
957 p_native =
"\\\\?\\";
958 p_native += p_fspath;
967 read_lendian_t(len,p_abort);
968 return read_string_ex(len,p_abort);
972 read_object(temp->
lock_buffer(p_len),p_len,p_abort);
978 void filesystem::remove_directory_content(
const char * path,
abort_callback & abort) {
982 if (p_is_subdirectory) p_owner->
list_directory(p_url, *
this, p_abort);
984 p_owner->
remove(p_url, p_abort);
985 }
catch(exception_io_not_found) {}
990 list_directory(path, cb, abort);
992 void filesystem::remove_object_recur(
const char * path,
abort_callback & abort) {
994 remove_directory_content(path, abort);
995 }
catch(exception_io_not_found) {}
999 void filesystem::g_remove_object_recur_timeout(
const char * path,
double timeout,
abort_callback & abort) {
1000 FB2K_RETRY_FILE_MOVE( g_remove_object_recur(path, abort), abort, timeout );
1004 g_get_interface(path)->remove_object_recur(path, abort);
1013 if (!p_is_subdirectory && p_stats.
m_timestamp < m_base) {
1015 filesystem::g_remove_timeout(p_url, 1, p_abort);
1016 }
catch(exception_io_not_found) {}
1024 myCallback cb(period);
1025 filesystem::g_list_directory(directory, cb, abort);
1029 enum { bufCount = 256 };
1030 char buffer[bufCount];
1034 char & c = buffer[w];
1035 this->read_object( &c, 1, abort );
1039 if (++w == bufCount ) {
1052 if (did != atOnce)
break;
1062 while(lim > 0 && fullString[lim-1] ==
' ') --lim;
1067 const t_size len = strlen(protocolName);
1069 return fullString[len] ==
':' && fullString[len+1] ==
'/' && fullString[len+2] ==
'/';
1072 const char * base = strstr(fullString,
"://");
1074 out = protocolName; out << base;
1076 PFC_ASSERT(!
"Should not get here");
virtual void remove(const char *p_path, abort_callback &p_abort)=0
void read(const service_ptr_t< file > &p_file, abort_callback &p_abort, pfc::string_base &p_out, bool &is_utf8)
t_filesize get_size(HANDLE p_handle)
bool matchContentType(const char *fullString, const char *ourType)
t_seek_mode
Seeking mode constants. Note: these are purposedly defined to same values as standard C SEEK_* consta...
t_int64 t_sfilesize
Type used for file size related variables when a signed value is needed.
const t_item * get_ptr() const
t_filesize m_size
Size of the file.
t_filetimestamp filetimestamp_from_system_timer()
A class providing abstraction for an open file object, with reading/writing/seeking methods...
PFC_NORETURN void win32_file_write_failure(DWORD p_code, const char *path)
This class is used to signal underlying worker code whether user has decided to abort a potentially t...
T min_t(const T &item1, const T &item2)
void seek(HANDLE p_handle, t_sfilesize p_position, file::t_seek_mode p_mode)
DWORD SHARED_EXPORT uGetFileAttributes(const char *fn)
virtual void add_string(const char *p_string, t_size p_length=~0)=0
PFC_NORETURN void exception_io_from_win32(DWORD p_code)
bool first(service_ptr_t< t_query > &p_out)
bool extract_native_path_ex(const char *p_fspath, pfc::string_base &p_native)
bool _extract_native_path_ptr(const char *&p_fspath)
static int path_unpack_string(pfc::string_base &out, const char *src)
rcptr_t< string_base const > t_data
static const t_filesize filesize_invalid
Invalid/unknown file size constant. Also see: t_filesize.
int stricmp_ascii_ex(const char *const s1, t_size const len1, const char *const s2, t_size const len2)
void check() const
Checks if user has requested the operation to be aborted, and throws exception_aborted if so...
bool is_native_filesystem(const char *p_fspath)
virtual void open(service_ptr_t< file > &p_out, const char *p_path, t_open_mode p_mode, abort_callback &p_abort)=0
t_size scan_filename() const
t_open_mode
Enumeration specifying how to open a file. See: filesystem::open(), filesystem::g_open().
void dynamic_assert(bool p_condition, const char *p_msg)
Interface for archive reader services. When implementing, derive from archive_impl rather than from d...
t_size strlen_max(const char *ptr, t_size max)
string8_fastalloc string_formatter
static const unsigned unpack_prefix_len
void set_size(t_size p_size)
bool matchProtocol(const char *fullString, const char *protocolName)
Dummy abort_callback that never gets aborted. Slightly more efficient than the regular one especiall...
t_size scan_filename(const char *ptr)
t_filetimestamp m_timestamp
Time of last file modification.
virtual void set_string(const char *p_string, t_size p_length=~0)
static void makeBuffer(pfc::array_t< uint8_t > &buffer, size_t size)
bool extract_native_path(const char *p_fspath, pfc::string_base &p_native)
void purgeOldFiles(const char *directory, t_filetimestamp period, abort_callback &abort)
void generate_temp_location_for_file(pfc::string_base &p_out, const char *p_origpath, const char *p_extension, const char *p_magic)
PFC_DECLARE_EXCEPTION(exception_bad_cuesheet, exception_io_data,"Invalid cuesheet")
void substituteProtocol(pfc::string_base &out, const char *fullString, const char *protocolName)
Entrypoint service for all filesystem operations. Implementation: standard implementations for local ...
static void g_open(service_ptr_t< file > &p_out, const service_ptr_t< file > &p_source, abort_callback &p_abort)
Static helper querying existing unpacker implementations until one that successfully opens specified ...
char * lock_buffer(t_size n)
void add_char(t_uint32 c)
t_uint64 t_filetimestamp
Type used for file timestamp related variables. 64-bit value representing the number of 100-nanosecon...
virtual void list_directory(const char *p_path, directory_callback &p_out, abort_callback &p_abort)=0
t_uint64 t_filesize
Type used for file size related variables.
void check_e() const
For compatibility with old code. Do not call.
New EXPERIMENTAL string class, allowing efficient copies and returning from functions. Does not implement the string_base interface so you still need string8 in many cases. Safe to pass between DLLs, but since a reference is used, objects possibly created by other DLLs must be released before owning DLLs are unloaded.
void set_size_discard(t_size p_size)
Stores file stats (size and timestamp).
t_size string_find_first(const char *p_string, char p_tofind, t_size p_start)
static const char unpack_prefix[]
static void path_pack_string(pfc::string_base &out, const char *src)
virtual void truncate(t_size len)=0
bool next(service_ptr_t< t_query > &p_out)
string8_t< pfc::alloc_fast_aggressive > string8_fastalloc