foobar2000 SDK  2015-08-03
filesystem.cpp
Go to the documentation of this file.
1 #include "foobar2000.h"
2 
3 static const char unpack_prefix[] = "unpack://";
4 static const unsigned unpack_prefix_len = 9;
5 
7 {
10  if (e.first(ptr)) do {
11  p->reopen(p_abort);
12  try {
13  ptr->open(p_out,p,p_abort);
14  return;
15  } catch(exception_io_data const &) {}
16  } while(e.next(ptr));
17  throw exception_io_data();
18 }
19 
20 void file::seek_probe(t_filesize p_position, abort_callback & p_abort) {
21  try { seek(p_position, p_abort); } catch(exception_io_seek_out_of_range) {throw exception_io_data();}
22 }
23 
24 void file::seek_ex(t_sfilesize p_position, file::t_seek_mode p_mode, abort_callback &p_abort) {
25  switch(p_mode) {
26  case seek_from_beginning:
27  seek(p_position,p_abort);
28  break;
29  case seek_from_current:
30  seek(p_position + get_position(p_abort),p_abort);
31  break;
32  case seek_from_eof:
33  seek(p_position + get_size_ex(p_abort),p_abort);
34  break;
35  default:
36  throw exception_io_data();
37  }
38 }
39 
40 static void makeBuffer(pfc::array_t<uint8_t> & buffer, size_t size) {
41  for(;;) {// Tolerant malloc - allocate a smaller buffer if we're unable to acquire the requested size.
42  try {
43  buffer.set_size_discard( size );
44  return;
45  } catch(std::bad_alloc) {
46  if (size < 256) throw;
47  size >>= 1;
48  }
49  }
50 }
51 
52 t_filesize file::g_transfer(stream_reader * p_src,stream_writer * p_dst,t_filesize p_bytes,abort_callback & p_abort) {
54  makeBuffer(temp, (t_size)pfc::min_t<t_filesize>(1024*1024*8,p_bytes));
55  void* ptr = temp.get_ptr();
56  t_filesize done = 0;
57  while(done<p_bytes) {
58  p_abort.check_e();
59  t_size delta = (t_size)pfc::min_t<t_filesize>(temp.get_size(),p_bytes-done);
60  delta = p_src->read(ptr,delta,p_abort);
61  if (delta<=0) break;
62  p_dst->write(ptr,delta,p_abort);
63  done += delta;
64  }
65  return done;
66 }
67 
68 void file::g_transfer_object(stream_reader * p_src,stream_writer * p_dst,t_filesize p_bytes,abort_callback & p_abort) {
69  if (g_transfer(p_src,p_dst,p_bytes,p_abort) != p_bytes)
70  throw exception_io_data_truncation();
71 }
72 
73 
74 void filesystem::g_get_canonical_path(const char * path,pfc::string_base & out)
75 {
76  TRACK_CALL_TEXT("filesystem::g_get_canonical_path");
77 
80  if (e.first(ptr)) do {
81  if (ptr->get_canonical_path(path,out)) return;
82  } while(e.next(ptr));
83  //no one wants to process this, let's copy over
84  out = path;
85 }
86 
87 void filesystem::g_get_display_path(const char * path,pfc::string_base & out)
88 {
89  TRACK_CALL_TEXT("filesystem::g_get_display_path");
91  if (!g_get_interface(ptr,path))
92  {
93  //no one wants to process this, let's copy over
94  out = path;
95  }
96  else
97  {
98  if (!ptr->get_display_path(path,out))
99  out = path;
100  }
101 }
102 
103 bool filesystem::g_get_native_path( const char * path, pfc::string_base & out) {
104  // Is proper file:// path?
105  if (foobar2000_io::extract_native_path( path, out ) ) return true;
106 
107  // Set anyway
108  out = path;
109 
110  // Maybe just a file:// less local path? Check for other protocol markers
111  // If no :// present, return true anyway
112  return strstr( path, "://" ) == NULL;
113 }
114 
115 filesystem::ptr filesystem::g_get_interface(const char * path) {
116  filesystem::ptr rv;
117  if (!g_get_interface(rv, path)) throw exception_io_no_handler_for_path();
118  return rv;
119 
120 }
121 bool filesystem::g_get_interface(service_ptr_t<filesystem> & p_out,const char * path)
122 {
125  if (e.first(ptr)) do {
126  if (ptr->is_our_path(path))
127  {
128  p_out = ptr;
129  return true;
130  }
131  } while(e.next(ptr));
132  return false;
133 }
134 
135 
136 void filesystem::g_open(service_ptr_t<file> & p_out,const char * path,t_open_mode mode,abort_callback & p_abort)
137 {
138  TRACK_CALL_TEXT("filesystem::g_open");
139  g_get_interface(path)->open(p_out,path,mode,p_abort);
140 }
141 
142 
143 void filesystem::g_open_timeout(service_ptr_t<file> & p_out,const char * p_path,t_open_mode p_mode,double p_timeout,abort_callback & p_abort) {
144  FB2K_RETRY_ON_SHARING_VIOLATION( g_open(p_out, p_path, p_mode, p_abort), p_abort, p_timeout);
145 }
146 
147 bool filesystem::g_exists(const char * p_path,abort_callback & p_abort)
148 {
149  t_filestats stats;
150  bool dummy;
151  try {
152  g_get_stats(p_path,stats,dummy,p_abort);
153  } catch(exception_io_not_found) {return false;}
154  return true;
155 }
156 
157 bool filesystem::g_exists_writeable(const char * p_path,abort_callback & p_abort)
158 {
159  t_filestats stats;
160  bool writeable;
161  try {
162  g_get_stats(p_path,stats,writeable,p_abort);
163  } catch(exception_io_not_found) {return false;}
164  return writeable;
165 }
166 
167 void filesystem::g_remove(const char * p_path,abort_callback & p_abort) {
168  g_get_interface(p_path)->remove(p_path,p_abort);
169 }
170 
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 );
173 }
174 
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 );
177 }
178 
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 );
181 }
182 
183 void filesystem::g_create_directory(const char * p_path,abort_callback & p_abort)
184 {
185  g_get_interface(p_path)->create_directory(p_path,p_abort);
186 }
187 
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);
194  return;
195  }
196  } while(e.next(ptr));
197  throw exception_io_no_handler_for_path();
198 }
199 
200 void filesystem::g_link(const char * p_src,const char * p_dst,abort_callback & p_abort) {
201  if (!foobar2000_io::_extract_native_path_ptr(p_src) || !foobar2000_io::_extract_native_path_ptr(p_dst)) throw exception_io_no_handler_for_path();
202  WIN32_IO_OP( CreateHardLink( pfc::stringcvt::string_os_from_utf8( p_dst ), pfc::stringcvt::string_os_from_utf8( p_src ), NULL) );
203 }
204 
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 );
207 }
208 
209 
210 void filesystem::g_list_directory(const char * p_path,directory_callback & p_out,abort_callback & p_abort)
211 {
212  TRACK_CALL_TEXT("filesystem::g_list_directory");
213  g_get_interface(p_path)->list_directory(p_path,p_out,p_abort);
214 }
215 
216 
217 static void path_pack_string(pfc::string_base & out,const char * src)
218 {
219  out.add_char('|');
220  out << (unsigned) strlen(src);
221  out.add_char('|');
222  out << src;
223  out.add_char('|');
224 }
225 
226 static int path_unpack_string(pfc::string_base & out,const char * src)
227 {
228  int ptr=0;
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;
234  ptr++;
235  int start = ptr;
236  while(ptr-start<len)
237  {
238  if (src[ptr]==0) return -1;
239  ptr++;
240  }
241  if (src[ptr]!='|') return -1;
242  out.set_string(&src[start],len);
243  ptr++;
244  return ptr;
245 }
246 
247 
248 void filesystem::g_open_precache(service_ptr_t<file> & p_out,const char * p_path,abort_callback & p_abort) {
249  service_ptr_t<filesystem> fs = g_get_interface(p_path);
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);
252 }
253 
254 bool filesystem::g_is_remote(const char * p_path) {
255  return g_get_interface(p_path)->is_remote(p_path);
256 }
257 
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);
261  else return false;
262 }
263 
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);
267  else return true;
268 }
269 
270 bool filesystem::g_relative_path_create(const char * file_path,const char * playlist_path,pfc::string_base & out)
271 {
272 
273  bool rv = false;
275 
276  if (g_get_interface(fs,file_path))
277  rv = fs->relative_path_create(file_path,playlist_path,out);
278 
279  return rv;
280 }
281 
282 bool filesystem::g_relative_path_parse(const char * relative_path,const char * playlist_path,pfc::string_base & out)
283 {
286  if (e.first(ptr)) do {
287  if (ptr->relative_path_parse(relative_path,playlist_path,out)) return true;
288  } while(e.next(ptr));
289  return false;
290 }
291 
292 
293 
294 bool archive_impl::get_canonical_path(const char * path,pfc::string_base & out)
295 {
296  if (is_our_path(path))
297  {
298  pfc::string8 archive,file,archive_canonical;
299  if (g_parse_unpack_path(path,archive,file))
300  {
301  g_get_canonical_path(archive,archive_canonical);
302  make_unpack_path(out,archive_canonical,file);
303 
304  return true;
305  }
306  else return false;
307  }
308  else return false;
309 }
310 
311 bool archive_impl::is_our_path(const char * path)
312 {
313  if (!g_is_unpack_path(path)) return false;
314  const char * type = get_archive_type();
315  path += 9;
316  while(*type)
317  {
318  if (*type!=*path) return false;
319  type++;
320  path++;
321  }
322  if (*path!='|') return false;
323  return true;
324 }
325 
326 bool archive_impl::get_display_path(const char * path,pfc::string_base & out)
327 {
329  if (g_parse_unpack_path(path,archive,file))
330  {
331  g_get_display_path(archive,out);
332  out.add_string("|");
333  out.add_string(file);
334  return true;
335  }
336  else return false;
337 }
338 
339 void archive_impl::open(service_ptr_t<file> & p_out,const char * path,t_open_mode mode, abort_callback & p_abort)
340 {
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);
345 }
346 
347 
348 void archive_impl::remove(const char * path,abort_callback & p_abort) {
349  throw exception_io_denied();
350 }
351 
352 void archive_impl::move(const char * src,const char * dst,abort_callback & p_abort) {
353  throw exception_io_denied();
354 }
355 
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();
360 }
361 
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))
365  {
366  pfc::string8 archive_rel;
367  if (g_relative_path_create(archive,playlist_path,archive_rel))
368  {
369  pfc::string8 out_path;
370  make_unpack_path(out_path,archive_rel,file);
371  out.set_string(out_path);
372  return true;
373  }
374  }
375  return false;
376 }
377 
378 bool archive_impl::relative_path_parse(const char * relative_path,const char * playlist_path,pfc::string_base & out)
379 {
380  if (!is_our_path(relative_path)) return false;
381  pfc::string8 archive_rel,file;
382  if (g_parse_unpack_path(relative_path,archive_rel,file))
383  {
385  if (g_relative_path_parse(archive_rel,playlist_path,archive))
386  {
387  pfc::string8 out_path;
388  make_unpack_path(out_path,archive,file);
389  out.set_string(out_path);
390  return true;
391  }
392  }
393  return false;
394 }
395 
396 bool archive_impl::g_parse_unpack_path_ex(const char * path,pfc::string_base & archive,pfc::string_base & file, pfc::string_base & type) {
397  PFC_ASSERT( g_is_unpack_path(path) );
398  const char * base = path + unpack_prefix_len; // strstr(path, "//");
399  const char * split = strchr(path,'|');
400  if (base == NULL || split == NULL || base > split) return false;
401  // base += 2;
402  type.set_string( base, split - base );
403  int delta = path_unpack_string(archive,split);
404  if (delta<0) return false;
405  split += delta;
406  file = split;
407  return true;
408 }
409 bool archive_impl::g_parse_unpack_path(const char * path,pfc::string_base & archive,pfc::string_base & file) {
410  PFC_ASSERT( g_is_unpack_path(path) );
411  path = strchr(path,'|');
412  if (!path) return false;
413  int delta = path_unpack_string(archive,path);
414  if (delta<0) return false;
415  path += delta;
416  file = path;
417  return true;
418 }
419 
420 bool archive_impl::g_is_unpack_path(const char * path) {
421  return strncmp(path,unpack_prefix,unpack_prefix_len) == 0;
422 }
423 
424 void archive_impl::g_make_unpack_path(pfc::string_base & path,const char * archive,const char * file,const char * name)
425 {
426  path = unpack_prefix;
427  path += name;
428  path_pack_string(path,archive);
429  path += file;
430 }
431 
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());}
433 
434 
435 FILE * filesystem::streamio_open(const char * path,const char * flags)
436 {
437  FILE * ret = 0;
438  pfc::string8 temp;
439  g_get_canonical_path(path,temp);
440  if (!strncmp(temp,"file://",7))
441  {
443  }
444  return ret;
445 }
446 
447 
448 namespace {
449 
450  class directory_callback_isempty : public directory_callback
451  {
452  bool m_isempty;
453  public:
454  directory_callback_isempty() : m_isempty(true) {}
455  bool on_entry(filesystem * owner,abort_callback & p_abort,const char * url,bool is_subdirectory,const t_filestats & p_stats)
456  {
457  m_isempty = false;
458  return false;
459  }
460  bool isempty() {return m_isempty;}
461  };
462 
463  class directory_callback_dummy : public directory_callback
464  {
465  public:
466  bool on_entry(filesystem * owner,abort_callback & p_abort,const char * url,bool is_subdirectory,const t_filestats & p_stats) {return false;}
467  };
468 
469 }
470 
471 bool filesystem::g_is_empty_directory(const char * path,abort_callback & p_abort)
472 {
473  directory_callback_isempty callback;
474  try {
475  g_list_directory(path,callback,p_abort);
476  } catch(exception_io const &) {return false;}
477  return callback.isempty();
478 }
479 
480 bool filesystem::g_is_valid_directory(const char * path,abort_callback & p_abort) {
481  try {
482  directory_callback_dummy cb;
483  g_list_directory(path,cb,p_abort);
484  return true;
485  } catch(exception_io const &) {return false;}
486 }
487 
488 bool directory_callback_impl::on_entry(filesystem * owner,abort_callback & p_abort,const char * url,bool is_subdirectory,const t_filestats & p_stats) {
489  p_abort.check_e();
490  if (is_subdirectory) {
491  if (m_recur) {
492  try {
493  owner->list_directory(url,*this,p_abort);
494  } catch(exception_io const &) {}
495  }
496  } else {
497  m_data.add_item(pfc::rcnew_t<t_entry>(url,p_stats));
498  }
499  return true;
500 }
501 
502 namespace {
503  class directory_callback_impl_copy : public directory_callback
504  {
505  public:
506  directory_callback_impl_copy(const char * p_target, filesystem::ptr fs) : m_fs(fs)
507  {
508  m_target = p_target;
509  m_target.fix_dir_separator('\\');
510  }
511 
512  bool on_entry(filesystem * owner,abort_callback & p_abort,const char * url,bool is_subdirectory,const t_filestats & p_stats) {
513  const char * fn = url + pfc::scan_filename(url);
514  t_size truncat = m_target.length();
515  m_target += fn;
516  if (is_subdirectory) {
517  try {
518  m_fs->create_directory(m_target,p_abort);
519  } catch(exception_io_already_exists) {}
520  m_target += "\\";
521  owner->list_directory(url,*this,p_abort);
522  } else {
523  _copy(url, m_target, owner, p_abort);
524  }
525  m_target.truncate(truncat);
526  return true;
527  }
528  void _copy(const char * src, const char * dst, filesystem * srcFS, abort_callback & p_abort) {
529  service_ptr_t<file> r_src,r_dst;
530  t_filesize size;
531 
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);
535 
536  if (size > 0) {
537  try {
538  file::g_transfer_object(r_src,r_dst,size,p_abort);
539  } catch(...) {
540  r_dst.release();
541  abort_callback_dummy dummy;
542  try {m_fs->remove(dst,dummy);} catch(...) {}
543  throw;
544  }
545  }
546  }
547  private:
548  pfc::string8_fastalloc m_target;
549  filesystem::ptr m_fs;
550  };
551 }
552 
553 void filesystem::copy_directory(const char * src, const char * dst, abort_callback & p_abort) {
554  try {
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);
559 }
560 
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);
563  try {
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);
568 }
569 
570 void filesystem::g_copy(const char * src,const char * dst,abort_callback & p_abort) {
571  service_ptr_t<file> r_src,r_dst;
572  t_filesize size;
573 
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);
577 
578  if (size > 0) {
579  try {
580  file::g_transfer_object(r_src,r_dst,size,p_abort);
581  } catch(...) {
582  r_dst.release();
583  abort_callback_dummy dummy;
584  try {g_remove(dst,dummy);} catch(...) {}
585  throw;
586  }
587  }
588 }
589 
590 void stream_reader::read_object(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
591  if (read(p_buffer,p_bytes,p_abort) != p_bytes) throw exception_io_data_truncation();
592 }
593 
594 t_filestats file::get_stats(abort_callback & p_abort)
595 {
596  t_filestats temp;
597  temp.m_size = get_size(p_abort);
598  temp.m_timestamp = get_timestamp(p_abort);
599  return temp;
600 }
601 
602 t_filesize stream_reader::skip(t_filesize p_bytes,abort_callback & p_abort)
603 {
604  t_uint8 temp[256];
605  t_filesize todo = p_bytes, done = 0;
606  while(todo > 0) {
607  t_size delta,deltadone;
608  delta = sizeof(temp);
609  if (delta > todo) delta = (t_size) todo;
610  deltadone = read(temp,delta,p_abort);
611  done += deltadone;
612  todo -= deltadone;
613  if (deltadone < delta) break;
614  }
615  return done;
616 }
617 
618 void stream_reader::skip_object(t_filesize p_bytes,abort_callback & p_abort) {
619  if (skip(p_bytes,p_abort) != p_bytes) throw exception_io_data_truncation();
620 }
621 
622 void filesystem::g_open_write_new(service_ptr_t<file> & p_out,const char * p_path,abort_callback & p_abort) {
623  g_open(p_out,p_path,open_mode_write_new,p_abort);
624 }
625 void file::g_transfer_file(const service_ptr_t<file> & p_from,const service_ptr_t<file> & p_to,abort_callback & p_abort) {
626  t_filesize length = p_from->get_size(p_abort);
627  p_from->reopen( p_abort );
628 // p_from->seek(0,p_abort);
629  p_to->seek(0,p_abort);
630  p_to->set_eof(p_abort);
631  if (length == filesize_invalid) {
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);
635  }
636 }
637 
638 void filesystem::g_open_temp(service_ptr_t<file> & p_out,abort_callback & p_abort) {
639  g_open(p_out,"tempfile://",open_mode_write_new,p_abort);
640 }
641 
642 void filesystem::g_open_tempmem(service_ptr_t<file> & p_out,abort_callback & p_abort) {
643  g_open(p_out,"tempmem://",open_mode_write_new,p_abort);
644 }
645 
646 file::ptr filesystem::g_open_tempmem() {
647  abort_callback_dummy aborter;
648  file::ptr f; g_open_tempmem(f, aborter); return f;
649 }
650 
651 void archive_impl::list_directory(const char * p_path,directory_callback & p_out,abort_callback & p_abort) {
652  throw exception_io_not_found();
653 }
654 
655 void archive_impl::create_directory(const char * path,abort_callback &) {
656  throw exception_io_denied();
657 }
658 
659 void filesystem::g_get_stats(const char * p_path,t_filestats & p_stats,bool & p_is_writeable,abort_callback & p_abort) {
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);
662 }
663 
664 void archive_impl::get_stats(const char * p_path,t_filestats & p_stats,bool & p_is_writeable,abort_callback & 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);
670  }
671  else throw exception_io_not_found();
672 }
673 
674 
675 bool file::is_eof(abort_callback & p_abort) {
676  t_filesize position,size;
677  position = get_position(p_abort);
678  size = get_size(p_abort);
679  if (size == filesize_invalid) return false;
680  return position >= size;
681 }
682 
683 
685 {
686  return pfc::fileTimeNow();
687 }
688 
689 void stream_reader::read_string_ex(pfc::string_base & p_out,t_size p_bytes,abort_callback & p_abort) {
690  const t_size expBase = 64*1024;
691  if (p_bytes > expBase) {
692  pfc::array_t<char> temp;
693  t_size allocWalk = expBase;
694  t_size done = 0;
695  for(;;) {
696  const t_size target = pfc::min_t(allocWalk, p_bytes);
697  temp.set_size(target);
698  read_object(temp.get_ptr() + done, target - done, p_abort);
699  if (target == p_bytes) break;
700  done = target;
701  allocWalk <<= 1;
702  }
703  p_out.set_string(temp.get_ptr(), p_bytes);
704  } else {
705  pfc::string_buffer buf(p_out, p_bytes);
706  read_object(buf.get_ptr(),p_bytes,p_abort);
707  }
708 }
709 void stream_reader::read_string(pfc::string_base & p_out,abort_callback & p_abort)
710 {
711  t_uint32 length;
712  read_lendian_t(length,p_abort);
713  read_string_ex(p_out,length,p_abort);
714 }
715 
716 void stream_reader::read_string_raw(pfc::string_base & p_out,abort_callback & p_abort) {
717  enum {delta = 256};
718  char buffer[delta];
719  p_out.reset();
720  for(;;) {
721  t_size delta_done;
722  delta_done = read(buffer,delta,p_abort);
723  p_out.add_string(buffer,delta_done);
724  if (delta_done < delta) break;
725  }
726 }
727 void stream_writer::write_string(const char * p_string,t_size p_len,abort_callback & p_abort) {
728  t_uint32 len = pfc::downcast_guarded<t_uint32>(pfc::strlen_max(p_string,p_len));
729  write_lendian_t(len,p_abort);
730  write_object(p_string,len,p_abort);
731 }
732 
733 void stream_writer::write_string(const char * p_string,abort_callback & p_abort) {
734  write_string(p_string,~0,p_abort);
735 }
736 
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);
739 }
740 
741 void file::truncate(t_uint64 p_position,abort_callback & p_abort) {
742  if (p_position < get_size(p_abort)) resize(p_position,p_abort);
743 }
744 
745 
746 #ifdef _WIN32
747 namespace {
748  //rare/weird win32 errors that didn't make it to the main API
749  PFC_DECLARE_EXCEPTION(exception_io_device_not_ready, exception_io,"Device not ready");
750  PFC_DECLARE_EXCEPTION(exception_io_invalid_drive, exception_io_not_found,"Drive not found");
751  PFC_DECLARE_EXCEPTION(exception_io_win32, exception_io,"Generic win32 I/O error");
752  PFC_DECLARE_EXCEPTION(exception_io_buffer_overflow, exception_io,"The file name is too long");
753  PFC_DECLARE_EXCEPTION(exception_io_invalid_path_syntax, exception_io,"Invalid path syntax");
754 
755  class exception_io_win32_ex : public exception_io_win32 {
756  public:
757  exception_io_win32_ex(DWORD p_code) : m_msg(pfc::string_formatter() << "I/O error (win32 #" << (t_uint32)p_code << ")") {}
758  exception_io_win32_ex(const exception_io_win32_ex & p_other) {*this = p_other;}
759  const char * what() const throw() {return m_msg;}
760  private:
761  pfc::string8 m_msg;
762  };
763 }
764 
765 PFC_NORETURN void foobar2000_io::win32_file_write_failure(DWORD p_code, const char * path) {
766  if (p_code == ERROR_ACCESS_DENIED) {
767  const DWORD attr = uGetFileAttributes(path);
768  if (attr != ~0 && (attr & FILE_ATTRIBUTE_READONLY) != 0) throw exception_io_denied_readonly();
769  }
770  exception_io_from_win32(p_code);
771 }
772 
773 PFC_NORETURN void foobar2000_io::exception_io_from_win32(DWORD p_code) {
774  //pfc::string_fixed_t<32> debugMsg; debugMsg << "Win32 I/O error #" << (t_uint32)p_code;
775  //TRACK_CALL_TEXT(debugMsg);
776  switch(p_code) {
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();
785  case ERROR_BUSY:
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:
797  case ERROR_NO_DATA:
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();
806  case ERROR_CRC:
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");
827  default:
828  throw exception_io_win32_ex(p_code);
829  }
830 }
831 #endif
832 
833 t_filesize file::get_size_ex(abort_callback & p_abort) {
834  t_filesize temp = get_size(p_abort);
835  if (temp == filesize_invalid) throw exception_io_no_length();
836  return temp;
837 }
838 
839 void file::ensure_local() {
840  if (is_remote()) throw exception_io_object_is_remote();
841 }
842 
843 void file::ensure_seekable() {
844  if (!can_seek()) throw exception_io_object_not_seekable();
845 }
846 
847 bool filesystem::g_is_recognized_path(const char * p_path) {
848  filesystem::ptr obj;
849  return g_get_interface(obj,p_path);
850 }
851 
852 t_filesize file::get_remaining(abort_callback & p_abort) {
853  t_filesize length = get_size_ex(p_abort);
854  t_filesize position = get_position(p_abort);
855  pfc::dynamic_assert(position <= length);
856  return length - position;
857 }
858 
859 void file::probe_remaining(t_filesize bytes, abort_callback & p_abort) {
860  t_filesize length = get_size(p_abort);
861  if (length != ~0) {
862  t_filesize remaining = length - get_position(p_abort);
863  if (remaining < bytes) throw exception_io_data_truncation();
864  }
865 }
866 
867 
868 t_filesize file::g_transfer(service_ptr_t<file> p_src,service_ptr_t<file> p_dst,t_filesize p_bytes,abort_callback & p_abort) {
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);
870 }
871 
872 void file::g_transfer_object(service_ptr_t<file> p_src,service_ptr_t<file> p_dst,t_filesize p_bytes,abort_callback & p_abort) {
873  if (p_bytes > 1024) /* don't bother on small objects */
874  {
875  t_filesize srcFileSize = p_src->get_size(p_abort); // detect truncation
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();
879  }
880 
881  t_filesize oldsize = p_dst->get_size(p_abort); // pre-resize the target file
882  if (oldsize != filesize_invalid) {
883  t_filesize newpos = p_dst->get_position(p_abort) + p_bytes;
884  if (newpos > oldsize) p_dst->resize(newpos ,p_abort);
885  }
886 
887  }
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);
889 }
890 
891 
892 void foobar2000_io::generate_temp_location_for_file(pfc::string_base & p_out, const char * p_origpath,const char * p_extension,const char * p_magic) {
893  hasher_md5_result hash;
894  {
896  hasher_md5_state state;
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);
902  }
903 
904  p_out = p_origpath;
905  p_out.truncate(p_out.scan_filename());
906  p_out += "temp-";
907  p_out += pfc::format_hexdump(hash.m_data,sizeof(hash.m_data),"");
908  p_out += ".";
909  p_out += p_extension;
910 }
911 
912 t_filesize file::skip_seek(t_filesize p_bytes,abort_callback & p_abort) {
913  const t_filesize size = get_size(p_abort);
914  if (size != filesize_invalid) {
915  const t_filesize position = get_position(p_abort);
916  const t_filesize toskip = pfc::min_t( p_bytes, size - position );
917  seek(position + toskip,p_abort);
918  return toskip;
919  } else {
920  this->seek_ex( p_bytes, seek_from_current, p_abort );
921  return p_bytes;
922  }
923 }
924 
925 t_filesize file::skip(t_filesize p_bytes,abort_callback & p_abort) {
926  if (p_bytes > 1024 && can_seek()) {
927  const t_filesize size = get_size(p_abort);
928  if (size != filesize_invalid) {
929  const t_filesize position = get_position(p_abort);
930  const t_filesize toskip = pfc::min_t( p_bytes, size - position );
931  seek(position + toskip,p_abort);
932  return toskip;
933  }
934  }
935  return stream_reader::skip(p_bytes,p_abort);
936 }
937 
938 bool foobar2000_io::is_native_filesystem( const char * p_fspath ) {
939  return _extract_native_path_ptr( p_fspath );
940 }
941 
942 bool foobar2000_io::_extract_native_path_ptr(const char * & p_fspath) {
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;
946  return true;
947 }
948 bool foobar2000_io::extract_native_path(const char * p_fspath,pfc::string_base & p_native) {
949  if (!_extract_native_path_ptr(p_fspath)) return false;
950  p_native = p_fspath;
951  return true;
952 }
953 
954 bool foobar2000_io::extract_native_path_ex(const char * p_fspath, pfc::string_base & p_native) {
955  if (!_extract_native_path_ptr(p_fspath)) return false;
956  if (p_fspath[0] != '\\' || p_fspath[1] != '\\') {
957  p_native = "\\\\?\\";
958  p_native += p_fspath;
959  } else {
960  p_native = p_fspath;
961  }
962  return true;
963 }
964 
965 pfc::string stream_reader::read_string(abort_callback & p_abort) {
966  t_uint32 len;
967  read_lendian_t(len,p_abort);
968  return read_string_ex(len,p_abort);
969 }
970 pfc::string stream_reader::read_string_ex(t_size p_len,abort_callback & p_abort) {
971  pfc::rcptr_t<pfc::string8> temp; temp.new_t();
972  read_object(temp->lock_buffer(p_len),p_len,p_abort);
973  temp->unlock_buffer();
974  return pfc::string::t_data(temp);
975 }
976 
977 
978 void filesystem::remove_directory_content(const char * path, abort_callback & abort) {
979  class myCallback : public directory_callback {
980  public:
981  bool on_entry(filesystem * p_owner,abort_callback & p_abort,const char * p_url,bool p_is_subdirectory,const t_filestats & p_stats) {
982  if (p_is_subdirectory) p_owner->list_directory(p_url, *this, p_abort);
983  try {
984  p_owner->remove(p_url, p_abort);
985  } catch(exception_io_not_found) {}
986  return true;
987  }
988  };
989  myCallback cb;
990  list_directory(path, cb, abort);
991 }
992 void filesystem::remove_object_recur(const char * path, abort_callback & abort) {
993  try {
994  remove_directory_content(path, abort);
995  } catch(exception_io_not_found) {}
996  remove(path, abort);
997 }
998 
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 );
1001 }
1002 
1003 void filesystem::g_remove_object_recur(const char * path, abort_callback & abort) {
1004  g_get_interface(path)->remove_object_recur(path, abort);
1005 }
1006 
1007 void foobar2000_io::purgeOldFiles(const char * directory, t_filetimestamp period, abort_callback & abort) {
1008 
1009  class myCallback : public directory_callback {
1010  public:
1011  myCallback(t_filetimestamp period) : m_base(filetimestamp_from_system_timer() - period) {}
1012  bool on_entry(filesystem * p_owner,abort_callback & p_abort,const char * p_url,bool p_is_subdirectory,const t_filestats & p_stats) {
1013  if (!p_is_subdirectory && p_stats.m_timestamp < m_base) {
1014  try {
1015  filesystem::g_remove_timeout(p_url, 1, p_abort);
1016  } catch(exception_io_not_found) {}
1017  }
1018  return true;
1019  }
1020  private:
1021  const t_filetimestamp m_base;
1022  };
1023 
1024  myCallback cb(period);
1025  filesystem::g_list_directory(directory, cb, abort);
1026 }
1027 
1028 void stream_reader::read_string_nullterm( pfc::string_base & out, abort_callback & abort ) {
1029  enum { bufCount = 256 };
1030  char buffer[bufCount];
1031  out.reset();
1032  size_t w = 0;
1033  for(;;) {
1034  char & c = buffer[w];
1035  this->read_object( &c, 1, abort );
1036  if (c == 0) {
1037  out.add_string( buffer, w ); break;
1038  }
1039  if (++w == bufCount ) {
1040  out.add_string( buffer, bufCount ); w = 0;
1041  }
1042  }
1043 }
1044 
1045 t_filesize stream_reader::skip_till_eof(abort_callback & abort) {
1046  t_filesize atOnce = 1024 * 1024;
1047  t_filesize done = 0;
1048  for (;; ) {
1049  abort.check();
1050  t_filesize did = this->skip(atOnce, abort);
1051  done += did;
1052  if (did != atOnce) break;
1053  }
1054  return done;
1055 }
1056 
1057 
1058 
1059 bool foobar2000_io::matchContentType(const char * fullString, const char * ourType) {
1060  t_size lim = pfc::string_find_first(fullString, ';');
1061  if (lim != ~0) {
1062  while(lim > 0 && fullString[lim-1] == ' ') --lim;
1063  }
1064  return pfc::stricmp_ascii_ex(fullString,lim, ourType, ~0) == 0;
1065 }
1066 bool foobar2000_io::matchProtocol(const char * fullString, const char * protocolName) {
1067  const t_size len = strlen(protocolName);
1068  if (pfc::stricmp_ascii_ex(fullString, len, protocolName, len) != 0) return false;
1069  return fullString[len] == ':' && fullString[len+1] == '/' && fullString[len+2] == '/';
1070 }
1071 void foobar2000_io::substituteProtocol(pfc::string_base & out, const char * fullString, const char * protocolName) {
1072  const char * base = strstr(fullString, "://");
1073  if (base) {
1074  out = protocolName; out << base;
1075  } else {
1076  PFC_ASSERT(!"Should not get here");
1077  out = fullString;
1078  }
1079 }
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...
Definition: filesystem.h:212
t_int64 t_sfilesize
Type used for file size related variables when a signed value is needed.
Definition: filesystem.h:10
const t_item * get_ptr() const
Definition: array.h:213
uint8_t t_uint8
Definition: int_types.h:9
t_filesize m_size
Size of the file.
Definition: filesystem.h:71
void new_t()
Definition: rcptr.h:101
t_filetimestamp filetimestamp_from_system_timer()
Definition: filesystem.cpp:684
A class providing abstraction for an open file object, with reading/writing/seeking methods...
Definition: filesystem.h:208
PFC_NORETURN void win32_file_write_failure(DWORD p_code, const char *path)
Definition: filesystem.cpp:765
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)
Definition: primitives.h:556
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
uint64_t t_uint64
Definition: int_types.h:3
char m_data[16]
Definition: hasher_md5.h:6
T * get_ptr() const
Definition: service.h:117
PFC_NORETURN void exception_io_from_win32(DWORD p_code)
Definition: filesystem.cpp:773
bool first(service_ptr_t< t_query > &p_out)
Definition: service.h:581
bool extract_native_path_ex(const char *p_fspath, pfc::string_base &p_native)
Definition: filesystem.cpp:954
bool _extract_native_path_ptr(const char *&p_fspath)
Definition: filesystem.cpp:942
void unlock_buffer()
Definition: string_base.h:417
static int path_unpack_string(pfc::string_base &out, const char *src)
Definition: filesystem.cpp:226
rcptr_t< string_base const > t_data
Definition: stringNew.h:21
static const t_filesize filesize_invalid
Invalid/unknown file size constant. Also see: t_filesize.
Definition: filesystem.h:16
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)
Definition: filesystem.cpp:938
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
Definition: string_base.h:235
t_open_mode
Enumeration specifying how to open a file. See: filesystem::open(), filesystem::g_open().
Definition: filesystem.h:387
void dynamic_assert(bool p_condition, const char *p_msg)
Definition: primitives.h:239
Interface for archive reader services. When implementing, derive from archive_impl rather than from d...
Definition: filesystem.h:519
t_size strlen_max(const char *ptr, t_size max)
Definition: string_base.h:91
size_t t_size
Definition: int_types.h:48
string8_fastalloc string_formatter
Definition: string_base.h:615
static const unsigned unpack_prefix_len
Definition: filesystem.cpp:4
void set_size(t_size p_size)
Definition: array.h:104
bool matchProtocol(const char *fullString, const char *protocolName)
void release()
Definition: service.h:109
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.
Definition: filesystem.h:73
virtual void set_string(const char *p_string, t_size p_length=~0)
Definition: string_base.h:198
static void makeBuffer(pfc::array_t< uint8_t > &buffer, size_t size)
Definition: filesystem.cpp:40
bool extract_native_path(const char *p_fspath, pfc::string_base &p_native)
Definition: filesystem.cpp:948
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)
Definition: filesystem.cpp:892
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 ...
Definition: filesystem.h:383
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 ...
Definition: filesystem.cpp:6
char * lock_buffer(t_size n)
Definition: string_base.h:409
void add_char(t_uint32 c)
Definition: string_base.cpp:5
t_uint64 t_filetimestamp
Type used for file timestamp related variables. 64-bit value representing the number of 100-nanosecon...
Definition: filesystem.h:12
virtual void list_directory(const char *p_path, directory_callback &p_out, abort_callback &p_abort)=0
uint64_t fileTimeNow()
Definition: timers.cpp:66
t_uint64 t_filesize
Type used for file size related variables.
Definition: filesystem.h:8
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.
Definition: stringNew.h:19
void set_size_discard(t_size p_size)
Definition: array.h:128
Stores file stats (size and timestamp).
Definition: filesystem.h:69
t_size string_find_first(const char *p_string, char p_tofind, t_size p_start)
static const char unpack_prefix[]
Definition: filesystem.cpp:3
static void path_pack_string(pfc::string_base &out, const char *src)
Definition: filesystem.cpp:217
virtual void truncate(t_size len)=0
t_size get_size() const
Definition: array.h:130
bool next(service_ptr_t< t_query > &p_out)
Definition: service.h:587
string8_t< pfc::alloc_fast_aggressive > string8_fastalloc
Definition: string_base.h:436
Decoder Validator v1 readme Usage Select a single file handled by the decoder you want to select Utils Decoder Utils Tag Writer Validator or Utils Fuzzer from the context menu with randomly generated info In case of the file will likely be permanently damaged or left with nonsense in its tags What Decoder Validator does Decoder Validator runs a series of automated decoding tests on the specified file
Definition: readme.txt:8
uint32_t t_uint32
Definition: int_types.h:5