YSTest: DSIO.cpp 源文件

YSTest PreAlpha_b800_20170810 The YSLib Test Project

- 首页
- 相关页面
- 模块
- 命名空间
- 类
- 文件

- 文件列表
- 文件成员

DSIO.cpp
浏览该文件的文档.
1 /*
2 © 2015-2017 FrankHB.
3
4 This file is part of the YSLib project, and may only be used,
5 modified, and distributed under the terms of the YSLib project
6 license, LICENSE.TXT. By continuing to use, modify, or distribute
7 this file you indicate that you have read the license and
8 understand and accept it fully.
9 */
10
29 #include "YCLib/YModules.h"
30 #include YFM_DS_YCLib_DSIO // for platform::Descriptions, std::runtime_error,
31 // ystdex::throw_error, std::system_error, string_view, ystdex::retry_on_cond,
32 // YAssertNonnull, FetchSeparator, std::bind, std::placeholder::_1,
33 // ystdex::exchange, ystdex::invoke, std::ref, YTraceDe, std::exception,
34 // ystdex::trivially_copy_n, ptrdiff_t, DISC_INTERFACE, unique_raw,
35 // ystdex::aligned_store_cast;
36 #if YCL_DS
37 # include "YSLib/Core/YModules.h"
38 # include YFM_YSLib_Core_YException // for YSLib::TryInvoke,
39 // YSLib::FilterExceptions;
40 # include <ystdex/functional.hpp> // for std::all_of,
41 // ystdex::common_nonvoid_t, ystdex::call_for_value;
42 # include "CHRLib/YModules.h"
43 # include YFM_CHRLib_CharacterProcessing // for ystdex::read_uint_le,
44 // ystdex::write_uint_le, CHRLib::MakeUCS2LE, ystdex::ntctsicmp,
45 // ystdex::ntctsncpy;
46 # include <cerrno> // for ENOMEM, ENOTSUP, EFBIG, EINVAL;
47 # include YFM_YCLib_NativeAPI // for OpenMode;
48 # include <ystdex/scope_guard.hpp> // for ystdex::make_guard;
49 # include <sys/iosupport.h> // for ::_reent, ::size_t, ::ssize_t, ::off_t,
50 // sturct ::stat, struct ::statvfs, ::DIR_ITER, ::devoptab_t;
51 #endif
52
53 namespace platform_ex
54 {
55
56 using namespace platform;
57 using namespace Descriptions;
58
59 #if YCL_DS
60 SectorCache::SectorCache(size_t pages, size_t spps, Disc d, ::sec_t eop,
61 size_t bps)
62 : disc(d), number_of_pages(std::max<size_t>(pages, 2)),
63 sectors_per_page_shift(std::max<size_t>(spps, 3)), end_of_partition(eop),
64 bytes_per_sector(bps),
65 entries(number_of_pages)
66 {
67 entries.flush = [this](UsedListCache::value_type& pr) ynothrow{
68 FlushEntry(pr);
69 };
70 }
71 SectorCache::~SectorCache()
72 {
73 Flush();
74 }
75
76 size_t
77 SectorCache::GetBlockCount(::sec_t sec) const ynothrowv
78 {
79 const auto end_sec(std::min<::sec_t>(
80 sec + (1 << sectors_per_page_shift), end_of_partition));
81
82 YAssert(sec <= end_sec, "Invalid range found.");
83 return end_sec - sec;
84 }
85 ystdex::block_buffer*
86 SectorCache::GetPage(::sec_t key) ynothrowv
87 {
88 YAssert(GetKey(key) == key, "Invalid key found.");
89
90 return YSLib::TryInvoke([=]{
91 return &ystdex::cache_lookup(entries, key, [=]{
92 const auto size(bytes_per_sector << sectors_per_page_shift);
93 // TODO: Use aligned allocation for cache implementation.
94 // NOTE: Intentionally uninitialized as libfat.
95 ystdex::block_buffer entry(make_unique_default_init<byte[]>(size));
96 const auto n(GetBlockCount(key));
97
98 YAssert(n <= size, "Invalid cache creation parameter found.");
99 if(YB_UNLIKELY(!disc.ReadSectors(key, n, entry.get())))
100 throw std::runtime_error(
101 "Failed reading on creating file system cache entry.");
102 return entry;
103 });
104 });
105 }
106
107 bool
108 SectorCache::EraseWritePartialSector(::sec_t sec, size_t offset,
109 const void* p_buf, size_t n) ynothrowv
110 {
111 return PerformPartialSectorIO([=](ystdex::block_buffer& entry,
112 size_t sec_bytes){
113 ystdex::trivially_fill_n(entry.get() + sec_bytes, bytes_per_sector);
114 entry.write(sec_bytes + offset, p_buf, n);
115 }, sec, offset, n);
116 }
117
118 bool
119 SectorCache::FillPartialSector(::sec_t sec, size_t offset, size_t n, byte val)
120 ynothrowv
121 {
122 return PerformPartialSectorIO([=](ystdex::block_buffer& entry,
123 size_t sec_bytes){
124 entry.fill(sec_bytes + offset, n, val);
125 }, sec, offset, n);
126 }
127
128 bool
129 SectorCache::FillSectors(::sec_t sec, size_t n, byte val) ynothrow
130 {
131 return PerformSectorsIO([&](ystdex::block_buffer& entry, size_t sec_off,
132 size_t secs_to_process){
133 entry.fill(sec_off * bytes_per_sector,
134 secs_to_process * bytes_per_sector, val);
135 }, sec, n);
136 }
137
138 bool
139 SectorCache::Flush() ynothrow
140 {
141 // TODO: Flush all?
142 return std::all_of(entries.begin(), entries.end(),
143 std::bind(&SectorCache::FlushEntry, this, std::placeholders::_1));
144 }
145
146 bool
147 SectorCache::FlushEntry(UsedListCache::value_type& pr) ynothrow
148 {
149 if(pr.second.modified())
150 {
151 if(!disc.WriteSectors(pr.first, GetBlockCount(pr.first),
152 pr.second.get()))
153 return {};
154 pr.second.flush();
155 }
156 return true;
157 }
158
159 bool
160 SectorCache::ReadPartialSector(void* p_buf, ::sec_t sec, size_t offset,
161 size_t n) ynothrowv
162 {
163 return PerformPartialSectorIO([=](ystdex::block_buffer& entry,
164 size_t sec_bytes){
165 entry.read(p_buf, sec_bytes + offset, n);
166 }, sec, offset, n);
167 }
168
169 bool
170 SectorCache::ReadSectors(void* p_buf, ::sec_t sec, size_t n) ynothrowv
171 {
172 auto dst(static_cast<byte*>(p_buf));
173
174 return PerformSectorsIO([&](ystdex::block_buffer& entry, size_t sec_off,
175 size_t secs_to_process){
176 entry.read(dst, sec_off * bytes_per_sector,
177 secs_to_process * bytes_per_sector);
178 dst += secs_to_process * bytes_per_sector;
179 }, sec, n);
180 }
181
182 bool
183 SectorCache::WritePartialSector(::sec_t sec, size_t offset, const void* p_buf,
184 size_t n) ynothrowv
185 {
186 return PerformPartialSectorIO([=](ystdex::block_buffer& entry,
187 size_t sec_bytes){
188 entry.write(sec_bytes + offset, p_buf, n);
189 }, sec, offset, n);
190 }
191
192 bool
193 SectorCache::WriteSectors(::sec_t sec, const void* p_buf, size_t n)
194 ynothrow
195 {
196 auto src(static_cast<const byte*>(p_buf));
197
198 return PerformSectorsIO([&](ystdex::block_buffer& entry, size_t sec_off,
199 size_t secs_to_process){
200 entry.write(sec_off * bytes_per_sector, src,
201 secs_to_process * bytes_per_sector);
202 src += secs_to_process * bytes_per_sector;
203 }, sec, n);
204 }
205
206 namespace FAT
207 {
208
210
211 using ystdex::read_uint_le;
212 using ystdex::write_uint_le;
213 using ystdex::throw_error;
215
216 using std::system_error;
217 using std::errc;
218 // XXX: Coupled string types.
219 using CHRLib::MakeUCS2LE;
221
222 namespace
223 {
224
225 template<size_t _vWidth>
226 bool
227 ReadLEVal(SectorCache& cache, std::uint32_t& value, ::sec_t sec, size_t offset)
228 ynothrow
229 {
230 static yconstexpr const size_t
231 n(_vWidth / std::numeric_limits<byte>::digits);
232 byte buf[n];
233
234 if(cache.ReadPartialSector(buf, sec, offset, n))
235 {
236 value = ystdex::read_uint_le<_vWidth>(buf);
237 return true;
238 }
239 return {};
240 }
241
242 template<size_t _vWidth>
243 bool
244 WriteLEVal(SectorCache& cache, std::uint32_t value, ::sec_t sec, size_t offset)
245 ynothrow
246 {
247 static yconstexpr const size_t
248 n(_vWidth / std::numeric_limits<byte>::digits);
249 byte buf[n]{};
250
251 write_uint_le<_vWidth>(buf, value);
252 return cache.WritePartialSector(sec, offset, buf, n);
253 }
254
255
257
258 void
259 CheckThrowEIO(bool b) ythrow(system_error)
260 {
261 if(YB_UNLIKELY(!b))
262 throw_error(errc::io_error);
263 }
264
266 void
267 CheckThrowENOSPC(bool b) ythrow(system_error)
268 {
269 if(YB_UNLIKELY(!b))
270 throw_error(errc::no_space_on_device);
271 }
272
273 inline bool
274 MatchFATSig(const void* p)
275 {
276 static yconstexpr const char FAT_SIG[]{'F', 'A', 'T'};
277
278 return std::memcmp(p, FAT_SIG, sizeof(FAT_SIG)) == 0;
279 }
280
281 template<class _tPos>
282 ::sec_t
283 PosToSec(const AllocationTable& tbl, const _tPos& pos) ynothrow
284 {
285 return tbl.ClusterToSector(pos.GetCluster()) + pos.GetSector();
286 }
288
290
291 PDefH(void, CheckFillPartialSectorOff, const AllocationTable& tbl,
292 const FilePosition& pos, size_t offset, size_t n) ythrow(system_error)
293 ImplExpr(CheckThrowEIO(
294 tbl.Cache.FillPartialSector(PosToSec(tbl, pos), offset, n)))
295
296 PDefH(void, CheckFillPartialSector, const AllocationTable& tbl,
297 const FilePosition& pos, size_t n) ythrow(system_error)
298 ImplExpr(CheckFillPartialSectorOff(tbl, pos, pos.GetByte(), n))
299
300 PDefH(void, CheckFillSectors, const AllocationTable& tbl,
301 const FilePosition& pos) ythrow(system_error)
302 ImplExpr(CheckThrowEIO(tbl.Cache.FillSectors(PosToSec(tbl, pos))))
303
304 template<class _tPos>
305 void
306 CheckReadPartialSectorOff(const AllocationTable& tbl, const _tPos& pos,
307 void* data, size_t offset, size_t n) ythrow(system_error)
308 {
309 CheckThrowEIO(
310 tbl.Cache.ReadPartialSector(data, PosToSec(tbl, pos), offset, n));
311 }
312
313 PDefH(void, CheckReadPartialSector, const AllocationTable& tbl,
314 const DEntryPosition& pos, void* data) ythrow(system_error)
315 ImplExpr(CheckReadPartialSectorOff(tbl, pos, data, pos.GetByte(),
316 EntryDataSize))
317 PDefH(void, CheckReadPartialSector, const AllocationTable& tbl,
318 const FilePosition& pos, void* data, size_t n) ythrow(system_error)
319 ImplExpr(CheckReadPartialSectorOff(tbl, pos, data, pos.GetByte(), n))
320
321 PDefH(void, CheckReadSectors, const AllocationTable& tbl, ClusterIndex c,
322 void* buf, size_t n) ythrow(system_error)
323 ImplExpr(CheckThrowEIO(
324 tbl.Cache.ReadSectors(buf, tbl.ClusterToSector(c), n)))
325 PDefH(void, CheckReadSectors, const AllocationTable& tbl,
326 const FilePosition& pos, void* buf, size_t n) ythrow(system_error)
327 ImplExpr(CheckThrowEIO(
328 tbl.Cache.ReadSectors(buf, PosToSec(tbl, pos), n)))
329
330 template<class _tPos>
331 void
332 CheckWritePartialSectorOff(const AllocationTable& tbl, const _tPos& pos,
333 const void* data, size_t offset, size_t n) ythrow(system_error)
334 {
335 CheckThrowEIO(
336 tbl.Cache.WritePartialSector(PosToSec(tbl, pos), offset, data, n));
337 }
338
339 PDefH(void, CheckWritePartialSector, const AllocationTable& tbl,
340 const DEntryPosition& pos, const void* data) ythrow(system_error)
341 ImplExpr(CheckWritePartialSectorOff(tbl, pos, data, pos.GetByte(),
342 EntryDataSize))
343 PDefH(void, CheckWritePartialSector, const AllocationTable& tbl, const
344 FilePosition& pos, const void* data, size_t n) ythrow(system_error)
345 ImplExpr(CheckWritePartialSectorOff(tbl, pos, data, pos.GetByte(), n))
346
347 PDefH(void, CheckWriteSectors, const AllocationTable& tbl, ClusterIndex c,
348 const void* buf, size_t n) ythrow(system_error) ImplRet(CheckThrowEIO(
349 tbl.Cache.WriteSectors(tbl.ClusterToSector(c), buf, n)))
350 PDefH(void, CheckWriteSectors, const AllocationTable& tbl, const FilePosition&
351 pos, const void* buf, size_t n) ythrow(system_error)
352 ImplRet(CheckThrowEIO(tbl.Cache.WriteSectors(PosToSec(tbl, pos), buf, n)))
354
356 bool
357 CompareEntryNames(EntryData& data, string_view comp, const string& name)
358 ynothrow
359 {
360 try
361 {
362 if(comp.length() == name.length()
363 && ystdex::ntctsicmp(MakeUCS2LE(comp).c_str(),
364 MakeUCS2LE(name).c_str(), comp.length()) == 0)
365 return true;
366 }
367 CatchIgnore(...)
368 try
369 {
370 if(data.FindAlias(comp))
371 return true;
372 }
373 CatchIgnore(...)
374 return {};
375 }
376
378 void
379 AssignValidCluster(Partition& part, FilePosition& pos, ClusterIndex next_start)
380 throw(system_error)
381 {
382 CheckThrowEIO(part.Table.IsValid(next_start));
383 pos = {next_start, 0, pos.GetByte()};
384 }
385
386 } // unnamed namespace;
388
389 AllocationTable::AllocationTable(::sec_t start_sector, const byte* sec_buf,
390 size_t pages, size_t sectors_per_page_shift, Disc d)
391 ynothrowv
392 : sectors_per_cluster(sec_buf[BPB_SecPerClus]),
393 bytes_per_sector([sec_buf, this]{
394 const auto t(read_uint_le<16>(sec_buf + BPB_BytsPerSec));
395
396 // TODO: Use interval API.
397 if(t < MinSectorSize || bytes_per_sector > MaxSectorSize)
398 throw std::runtime_error("Unsupported sector size found.");
399 return t;
400 }()), bytes_per_cluster(bytes_per_sector * sectors_per_cluster),
401 table_start(start_sector + read_uint_le<16>(sec_buf + BPB_RsvdSecCnt)),
402 table_size([sec_buf]() ynothrowv{
403 // NOTE: last_cluster = cluster_count + Clusters::First - 1.
404 const auto t(read_uint_le<16>(sec_buf + BPB_FATSz16));
405
406 return t == 0 ? size_t(
407 read_uint_le<32>(sec_buf + BPB_FATSz32)) : size_t(t);
408 }()), total_sectors_num([sec_buf]() ynothrowv{
409 const auto t(read_uint_le<16>(sec_buf + BPB_TotSec16));
410
411 return t == 0 ? read_uint_le<32>(sec_buf + BPB_TotSec32)
412 : std::uint32_t(t);
413 }()), limit(total_sectors_num / sectors_per_cluster),
414 root_dir_start(table_start + sec_buf[BPB_NumFATs] * table_size),
415 // NOTE: See Microsoft FAT Specification Section 3.5.
416 root_dir_sectors_num((read_uint_le<16>(sec_buf + BPB_RootEntCnt)
417 * EntryDataSize + (bytes_per_sector - 1)) / bytes_per_sector),
418 data_sectors_num(total_sectors_num - size_t(root_dir_start
419 + root_dir_sectors_num - start_sector)),
420 fs_type([start_sector, this]() ynothrowv -> FileSystemType{
421 // NOTE: See Microsoft FAT Specification Section 3.5.
422 ClusterIndex cluster_count(data_sectors_num / sectors_per_cluster);
423
424 last_cluster = cluster_count + Clusters::First - 1;
425 // TODO: Distinguish part type (e.g. FAT16B) with file system type.
426 if(cluster_count < Clusters::PerFAT12)
427 return FileSystemType::FAT12;
428 if(cluster_count < Clusters::PerFAT16)
429 return FileSystemType::FAT16;
430 return FileSystemType::FAT32;
431 }()), root_dir_cluster([sec_buf, this]() ynothrowv -> ClusterIndex{
432 if(fs_type != FileSystemType::FAT32)
433 return Clusters::FAT16RootDirectory;
434 // NOTE: Check if FAT mirroring is enabled.
435 if((sec_buf[BPB_ExtFlags] & 0x80) == 0)
436 // NOTE: Use the active FAT.
437 table_start += table_size * (sec_buf[BPB_ExtFlags] & 0x0F);
438 return read_uint_le<32>(sec_buf + BPB_RootClus);
439 }()), Cache(pages, sectors_per_page_shift, d,
440 start_sector + total_sectors_num, bytes_per_sector)
441 {}
442
443 bool
444 AllocationTable::ClearLinks(ClusterIndex c) ynothrow
445 {
446 if(IsValid(c))
447 {
448 if(c < first_free)
449 first_free = c;
450 while(IsValid(c))
451 {
452 WriteEntry(ystdex::exchange(c, QueryNext(c)), Clusters::Free);
453 if(free_cluster < limit)
454 ++free_cluster;
455 }
456 return true;
457 }
458 return {};
459 }
460
461 ClusterIndex
462 AllocationTable::CountFreeCluster() const ynothrow
463 {
464 size_t count(0);
465
466 for(ClusterIndex cur_cluster(Clusters::First); cur_cluster <= last_cluster;
467 ++cur_cluster)
468 if(QueryNext(cur_cluster) == Clusters::Free)
469 ++count;
470 return count;
471 }
472
473 ClusterIndex
474 AllocationTable::LinkFree(ClusterIndex c) ythrow(system_error)
475 {
476 // TODO: Remove redundant check of Clusters::Error.
477 if(c <= last_cluster)
478 {
479 const auto cur_link(QueryNext(c));
480
481 if(IsValid(cur_link))
482 {
483 if(cur_link != Clusters::Error)
484 return cur_link;
485 else
486 throw_error(errc::no_space_on_device);
487 }
488
489 auto first_valid(std::max<ClusterIndex>(first_free, Clusters::First));
490 bool loop_around = {};
491
492 while(QueryNext(first_valid) != Clusters::Free)
493 if(last_cluster < ++first_valid)
494 {
495 if(loop_around)
496 {
497 first_free = first_valid;
498 throw_error(errc::no_space_on_device);
499 }
500 else
501 {
502 first_valid = Clusters::First;
503 loop_around = true;
504 }
505 }
506 first_free = first_valid;
507 if(free_cluster != 0)
508 --free_cluster;
509 last_alloc_cluster = first_valid;
510 if(IsValid(c))
511 WriteEntry(c, first_valid);
512 WriteEntry(first_valid, Clusters::EndOfFile);
513 if(IsValid(first_valid) && first_valid != Clusters::Error)
514 return first_valid;
515 }
516 throw_error(errc::no_space_on_device);
517 }
518
519 ClusterIndex
520 AllocationTable::QueryNext(ClusterIndex c) const ynothrowv
521 {
522 YAssert(IsFreeOrValid(c), "Invalid allocated cluster found.");
523
524 ClusterIndex next_cluster(Clusters::Free);
525
526 if(c != Clusters::Free)
527 switch(fs_type)
528 {
529 case FileSystemType::FAT12:
530 {
531 const auto c32(c * 3 / 2);
532 ::sec_t sector(table_start + c32 / bytes_per_sector);
533 size_t offset(c32 % bytes_per_sector);
534
535 ReadLEVal<8>(Cache, next_cluster, sector, offset);
536 if(++offset == bytes_per_sector)
537 {
538 offset = 0;
539 ++sector;
540 }
541
542 std::uint32_t next_cluster_h(0);
543
544 ReadLEVal<8>(Cache, next_cluster_h, sector, offset);
545 next_cluster |= next_cluster_h << 8;
546 }
547 if(c & 0x01)
548 next_cluster = next_cluster >> 4;
549 else
550 next_cluster &= 0x0FFF;
551 if(Clusters::MaxValid12 < next_cluster)
552 next_cluster = Clusters::EndOfFile;
553 break;
554 case FileSystemType::FAT16:
555 ReadLEVal<16>(Cache, next_cluster, table_start + (c << 1)
556 / bytes_per_sector, c % (bytes_per_sector >> 1) << 1);
557 if(Clusters::MaxValid16 < next_cluster)
558 next_cluster = Clusters::EndOfFile;
559 break;
560 case FileSystemType::FAT32:
561 ReadLEVal<32>(Cache, next_cluster, table_start + (c << 2)
562 / bytes_per_sector, c % (bytes_per_sector >> 2) << 2);
563 if(Clusters::MaxValid32 < next_cluster)
564 next_cluster = Clusters::EndOfFile;
565 break;
566 default:
567 return Clusters::Error;
568 }
569 return next_cluster;
570 }
571
572 ClusterIndex
573 AllocationTable::QueryLast(ClusterIndex c) const ynothrow
574 {
575 return ystdex::retry_on_cond([&, this]() ynothrow{
576 return IsValid(c);
577 }, [&]() ynothrow{
578 return ystdex::exchange(c, QueryNext(c));
579 });
580 }
581
582 void
583 AllocationTable::ReadClusters(const byte* sec_buf) ynothrowv
584 {
585 yunseq(
586 free_cluster = read_uint_le<32>(sec_buf + FSI_Free_Count),
587 last_alloc_cluster = read_uint_le<32>(sec_buf + FSI_Nxt_Free)
588 );
589 }
590
591 ClusterIndex
592 AllocationTable::TrimChain(ClusterIndex start, size_t len) ythrow(system_error)
593 {
594 auto next(QueryNext(start));
595
596 while(len-- != 0)
597 {
598 CheckThrowEIO(IsValid(next));
599 start = next;
600 next = QueryNext(start);
601 }
602 if(IsValid(next))
603 ClearLinks(next);
604 WriteEntry(start, Clusters::EndOfFile);
605 return start;
606 }
607
608 ClusterIndex
609 AllocationTable::LinkFreeCleared(ClusterIndex c) ythrow(system_error)
610 {
611 const auto t(LinkFree(c));
612
613 YAssert(t != Clusters::Error, "Invalid cluster found.");
614 for(size_t i(0); i < sectors_per_cluster; ++i)
615 CheckThrowEIO(Cache.FillSectors(ClusterToSector(t) + i));
616 return t;
617 }
618
619 void
620 AllocationTable::WriteClusters(byte* sec_buf) const ynothrowv
621 {
622 write_uint_le<32>(sec_buf + FSI_Free_Count, free_cluster),
623 write_uint_le<32>(sec_buf + FSI_Nxt_Free, last_alloc_cluster);
624 }
625
626 bool
627 AllocationTable::WriteEntry(ClusterIndex c, std::uint32_t val) const ynothrow
628 {
629 if(IsValid(c))
630 {
631 switch(fs_type)
632 {
633 case FileSystemType::FAT12:
634 {
635 const auto c32(c * 3 / 2);
636 ::sec_t sector(table_start + (c32 / bytes_per_sector));
637 size_t offset(c32 % bytes_per_sector);
638 std::uint32_t old_val;
639
640 if((c & 0x01) != 0)
641 {
642 ReadLEVal<8>(Cache, old_val, sector, offset);
643 val = val << 4 | (old_val & 0x0F);
644 WriteLEVal<8>(Cache, byte(val), sector, offset);
645 if(++offset >= bytes_per_sector)
646 yunseq(offset = 0, ++sector);
647 WriteLEVal<8>(Cache, byte(val >> 8), sector, offset);
648 }
649 else
650 {
651 WriteLEVal<8>(Cache, val, sector, offset);
652 if(++offset >= bytes_per_sector)
653 yunseq(offset = 0, ++sector);
654 ReadLEVal<8>(Cache, old_val, sector, offset);
655 val = ((val >> 8) & 0x0F) | (old_val & 0xF0);
656 WriteLEVal<8>(Cache, val, sector, offset);
657 }
658 }
659 break;
660 case FileSystemType::FAT16:
661 WriteLEVal<16>(Cache, val, table_start + (c << 1)
662 / bytes_per_sector, (c % (bytes_per_sector >> 1)) << 1);
663 break;
664 case FileSystemType::FAT32:
665 WriteLEVal<32>(Cache, val, table_start + (c << 2)
666 / bytes_per_sector, (c % (bytes_per_sector >> 2)) << 2);
667 break;
668 default:
669 return {};
670 }
671 return true;
672 }
673 return {};
674 }
675
676 void
677 AllocationTable::WriteFSInfo(byte* sec_buf) ynothrowv
678 {
679 YAssertNonnull(sec_buf);
680 // FIXME: Big endian implementations.
681 write_uint_le<32>(sec_buf + FSI_LeadSig, FSI_LeadSig_Value),
682 write_uint_le<32>(sec_buf + FSI_StrucSig, FSI_StrucSig_Value);
683 free_cluster = CountFreeCluster();
684 WriteClusters(sec_buf);
685 write_uint_le<32>(sec_buf + FSI_TrailSig, FSI_TrailSig_Value);
686 }
687
688
689 void
690 FilePosition::Decompose(std::uint32_t pos, size_t bytes_per_cluster,
691 size_t bytes_per_sector) ynothrowv
692 {
693 YAssert(bytes_per_cluster != 0, "Invalid parameter found."),
694 YAssert(bytes_per_sector != 0, "Invalid parameter found.");
695 yunseq(sector = pos % bytes_per_cluster / bytes_per_sector,
696 byte = pos % bytes_per_sector);
697 }
698
699
700 DEntry::DEntry(Partition& part)
701 : NamePos{DEntryPosition{0}, DEntryPosition{0}}, name(".")
702 {
703 // TODO: Optimize?
704 Data.SetupRoot(part.GetRootDirCluster());
705 }
706 DEntry::DEntry(Partition& part, const NamePosition& name_pos)
707 : NamePos(name_pos), name([&]{
708 auto pos(name_pos[0]);
709 char16_t long_name[LFN::MaxLength];
710
711 ystdex::retry_on_cond([&](bool finished){
712 part.ExtendPosition(pos);
713 return !finished;
714 }, [&]() -> bool{
715 EntryData edata;
716
717 CheckReadPartialSector(part.Table, pos, edata.data());
718 if(pos == name_pos[1])
719 return Data = edata, true;
720 else
721 edata.CopyLFN(long_name);
722 return {};
723 });
724 return name_pos[0] == name_pos[1] ? Data.GenerateAlias()
725 : LFN::ConvertToMBCS(long_name);
726 }())
727 {}
728 DEntry::DEntry(Partition& part, string_view sv, LeafAction act,
729 std::function<void(DEntry&)> add_entry, ClusterIndex& dclus)
730 {
731 YAssertNonnull(sv.data());
732 if(!sv.empty() && sv.front() == FetchSeparator<char>())
733 {
734 const auto pos(sv.find_first_not_of(FetchSeparator<char>()));
735
736 if(pos == string_view::npos)
737 goto found_root;
738 sv.remove_prefix(pos),
739 dclus = part.GetRootDirCluster();
740 }
741 else
742 dclus = part.GetCWDCluster();
743 ystdex::retry_on_cond(ystdex::logical_not<>(), [&]() -> bool{
744 const auto next_pos(sv.find(FetchSeparator<char>()));
745 const bool leaf(next_pos == string_view::npos);
746 string_view comp(sv.data(), leaf ? sv.length() : next_pos);
747
748 if(LFN::MaxMBCSLength < comp.length())
749 throw_error(errc::filename_too_long);
750 if(dclus == part.GetRootDirCluster() && (comp == "." || comp == ".."))
751 {
752 if(leaf && add_entry)
753 throw_error(errc::invalid_argument);
754 this->~DEntry();
755 ::new(this) DEntry(part);
756 }
757 else
758 {
759 name = ystdex::rtrim(comp, ' ').to_string();
760 if(leaf && add_entry && act != LeafAction::ThrowExisted)
761 return true;
762 NamePos = GenerateBeforeFirstNamePos(dclus);
763
764 string comp_name;
765
766 if(leaf && add_entry)
767 comp_name = name;
768 ystdex::retry_on_cond(ystdex::logical_not<>(),
769 [&, this](bool last_comp) -> bool{
770 if(!QueryNextFrom(part))
771 {
772 if(last_comp && add_entry)
773 {
774 name = comp_name;
775 return true;
776 }
777 throw_error(errc::no_such_file_or_directory);
778 }
779 return (last_comp || Data.IsDirectory())
780 && CompareEntryNames(Data, comp, name);
781 }, leaf);
782 }
783 if(leaf)
784 {
785 if(act == LeafAction::ThrowExisted)
786 {
787 if(add_entry)
788 return true;
789 throw_error(errc::file_exists);
790 }
791 if(act == LeafAction::Return)
792 return true;
793 }
794 if(Data.IsDirectory())
795 {
796 dclus = part.ReadClusterFromEntry(Data);
797 if(dclus == Clusters::Root)
798 dclus = part.GetRootDirCluster();
799 if(leaf)
800 return true;
801 sv.remove_prefix(next_pos);
802 sv.remove_prefix(std::min(
803 sv.find_first_not_of(FetchSeparator<char>()), sv.length()));
804 return sv.empty();
805 }
806 throw_error(errc::not_a_directory);
807 });
808 if(add_entry)
809 {
810 if(part.IsReadOnly())
811 throw_error(errc::read_only_file_system);
812 # if false
813 ystdex::ltrim(name, ' ');
814 # endif
815 if(YB_UNLIKELY(LFN::MaxMBCSLength < name.length()))
816 throw_error(errc::filename_too_long);
817 if(name.empty()
818 || name.find_first_of(LFN::IllegalCharacters) != string::npos)
819 throw_error(errc::invalid_argument);
820 add_entry(*this);
821
822 const auto name_len(name.length());
823
824 YAssert(name_len == ystdex::ntctslen(name.c_str())
825 && !(LFN::MaxMBCSLength < name_len) && name_len != 0
826 && name.find_first_of(LFN::IllegalCharacters)
827 == string::npos, "Invalid name found.");
828 if(YB_UNLIKELY(!LFN::ValidateName(name)))
829 throw_error(errc::invalid_argument);
830 if(!part.EntryExists(name, dclus))
831 {
832 EntryDataUnit alias_check_sum(0);
833 size_t entry_size;
834
835 std::tie(alias_check_sum, entry_size) = Data.FillNewName(name,
836 std::bind(&Partition::EntryExists, &part, std::placeholders::_1,
837 std::ref(dclus)));
838 FindEntryGap(part, dclus, entry_size);
839 YAssert(entry_size != 0, "Invalid entry size found.");
840
841 auto cur_entry_pos(NamePos[0]);
842 EntryData long_name_entry;
843 const auto& long_name(MakeUCS2LE(name));
844 size_t i(entry_size);
845
846 ystdex::retry_on_cond([&](bool cont) ynothrow{
847 return part.IncrementPosition(cur_entry_pos)
848 == ExtensionResult::Success && cont;
849 }, [&]() -> bool{
850 if(i > 1)
851 {
852 long_name_entry[LFN::Ordinal] = (i - 1)
853 | (i == entry_size ? LFN::LastLongEntry : 0);
854 for(size_t j(0); j < LFN::EntryLength; ++j)
855 // NOTE: Padding vs terminating null character.
856 write_uint_le<16>(long_name_entry.data()
857 + LFN::OffsetTable[j], long_name[(i - 2)
858 * LFN::EntryLength + j] == char16_t() ? (j > 1
859 && long_name[(i - 2) * LFN::EntryLength + j - 1]
860 == char16_t() ? 0xFFFF : 0x0000)
861 : long_name[(i - 2) * LFN::EntryLength + j]);
862 yunseq(
863 long_name_entry[LFN::CheckSum] = alias_check_sum,
864 long_name_entry[LFN::Flag]
865 = EntryDataUnit(Attribute::LongFileName),
866 long_name_entry[LFN::Reserved1] = 0
867 );
868 write_uint_le<16>(
869 long_name_entry.data() + LFN::Reserved2, 0);
870 CheckWritePartialSector(part.Table, cur_entry_pos,
871 long_name_entry.data());
872 --i;
873 return true;
874 }
875 else
876 CheckWritePartialSector(part.Table, cur_entry_pos,
877 Data.data());
878 return {};
879 });
880 return;
881 }
882 else
883 throw_error(errc::file_exists);
884 }
885 // NOTE: On FAT32 an actual cluster should be specified for the root
886 // entry, not cluster 0 as on FAT16.
887 if(part.GetFileSystemType() == FileSystemType::FAT32 && Data.IsDirectory()
888 && part.ReadClusterFromEntry(Data) == Clusters::Root)
889 {
890 found_root:
891 if(add_entry)
892 throw_error(errc::invalid_argument);
893 this->~DEntry();
894 ::new(this) DEntry(part);
895 if(act == LeafAction::EnsureDirectory)
896 dclus = part.GetRootDirCluster();
897 }
898 }
899
900 void
901 DEntry::FindEntryGap(Partition& part, ClusterIndex dclus, size_t size)
902 ythrow(system_error)
903 {
904 // NOTE: Scan for free entry.
905 DEntryPosition gap_end(dclus), gap_start(gap_end);
906 bool end_of_dir{};
907 auto dentry_remain(size);
908 EntryData edata;
909
910 if(size != 0)
911 while(true)
912 {
913 CheckReadPartialSector(part.Table, gap_end, edata.data());
914 if(edata[0] == EntryData::Last || edata[0] == EntryData::Free)
915 {
916 if(dentry_remain == size)
917 gap_start = gap_end;
918 --dentry_remain;
919 end_of_dir = edata[0] == EntryData::Last;
920 }
921 else
922 dentry_remain = size;
923 if(!end_of_dir && dentry_remain > 0)
924 part.ExtendPosition(gap_end);
925 else
926 break;
927 }
928 NamePos[0] = gap_start;
929 if(end_of_dir)
930 {
931 edata.FillLast();
932 // NOTE: Increase by one to take account of End Of Directory Marker.
933 ++dentry_remain;
934 while(dentry_remain > 0)
935 {
936 NamePos[1] = gap_end;
937 part.ExtendPosition(gap_end);
938 // NOTE: Fill the entry with blanks.
939 CheckWritePartialSector(part.Table, gap_end, edata.data());
940 --dentry_remain;
941 }
942 }
943 else
944 NamePos[1] = gap_end;
945 }
946
947 bool
948 DEntry::QueryNextFrom(Partition& part) ythrow(system_error)
949 {
950 auto estart(NamePos[1]);
951
952 if(estart.IsFAT16RootCluster())
953 estart.SetCluster(part.GetRootDirCluster());
954
955 auto eend(estart);
956 EntryData edata;
957 char16_t long_name[LFN::MaxLength];
958 bool has_long_name = {};
959 EntryDataUnit chk_sum = 0;
960
961 while(part.IncrementPosition(eend) == ExtensionResult::Success)
962 {
963 CheckReadPartialSector(part.Table, eend, edata.data());
964 if(edata.IsLongFileName())
965 {
966 const auto ord(edata[LFN::Ordinal]);
967
968 // NOTE: See Microsoft FAT Specification Section 7.1.
969 if((ord & 0x80) != 0)
970 // NOTE: Corrupted.
971 has_long_name = {};
972 else if((ord & LFN::LastLongEntry) != 0)
973 yunseq(
974 estart = eend, has_long_name = true,
975 long_name[std::min<size_t>((ord & ~LFN::LastLongEntry)
976 * LFN::EntryLength, LFN::MaxLength - 1)] = char16_t(),
977 chk_sum = edata[LFN::CheckSum]
978 );
979 else if(chk_sum != edata[LFN::CheckSum])
980 has_long_name = {};
981 if(has_long_name)
982 edata.CopyLFN(long_name);
983 }
984 else if(edata.IsVolume())
985 ;
986 else if(edata[0] == EntryData::Last)
987 break;
988 else if(edata[0] != EntryData::Free && edata[0] > 0x20)
989 {
990 if(has_long_name
991 && LFN::GenerateAliasChecksum(edata.data()) != chk_sum)
992 {
993 has_long_name = {};
994 name.clear();
995 }
996 if(has_long_name)
997 TryExpr(name = LFN::ConvertToMBCS(long_name))
998 CatchRet(..., {})
999 else
1000 {
1001 estart = eend;
1002 TryExpr(name = edata.GenerateAlias())
1003 CatchExpr(..., name.clear())
1004 }
1005 yunseq(NamePos[0] = estart, NamePos[1] = eend, Data = edata);
1006 return true;
1007 }
1008 }
1009 return {};
1010 }
1011
1012
1013 Partition::Partition(Disc d, size_t pages, size_t sectors_per_page_shift,
1014 ::sec_t start_sector)
1015 // TODO: Use aligned allocation.
1016 // NOTE: Uninitialized intentionally here to make it behave as libfat.
1017 : Partition(make_unique_default_init<byte[]>(MaxSectorSize).get(), d, pages,
1018 sectors_per_page_shift, start_sector)
1019 {}
1020 Partition::Partition(byte* sec_buf, Disc d, size_t pages,
1021 size_t sectors_per_page_shift, ::sec_t start_sector)
1022 : disc(d), Table([&, sec_buf]{
1023 if(!ReadSector(start_sector, sec_buf))
1024 throw std::runtime_error("Failed reading sectors.");
1025 if(sec_buf[Signature_word] != 0x55
1026 || sec_buf[Signature_word + 1] != 0xAA)
1027 throw std::runtime_error("Invalid MBR or boot sector found.");
1028 if(start_sector == 0 && !MatchFATSig(sec_buf + FAT16::BS_FilSysType)
1029 && !MatchFATSig(sec_buf + FAT32::BS_FilSysType))
1030 {
1031 start_sector = FindFirstValidPartition(sec_buf);
1032 if(!ReadSector(start_sector, sec_buf))
1033 throw std::runtime_error(
1034 "Failed reading sectors for finding start sector.");
1035 }
1036 if(!MatchFATSig(sec_buf + FAT16::BS_FilSysType)
1037 && !MatchFATSig(sec_buf + FAT32::BS_FilSysType))
1038 throw std::runtime_error("Failed verifying FAT part.");
1039 GetMutexRef().lock();
1040 ystdex::ntctsncpy(label.data(), sec_buf + (MatchFATSig(sec_buf
1041 + FAT16::BS_FilSysType) ? size_t(FAT16::BS_VolLab)
1042 : size_t(FAT32::BS_VolLab)), label.size());
1043 return start_sector;
1044 }(), sec_buf, pages, sectors_per_page_shift, d),
1045 fs_info_sector(start_sector + (read_uint_le<16>(sec_buf + BPB_FSInfo)
1046 ? read_uint_le<16>(sec_buf + BPB_FSInfo) : 1)),
1047 read_only((disc.GetFeatures() & FEATURE_MEDIUM_CANWRITE) == 0),
1048 cwd_cluster(GetRootDirCluster())
1049 {
1050 if(GetFileSystemType() == FileSystemType::FAT32)
1051 ReadFSInfo();
1052 GetMutexRef().unlock();
1053 }
1054 Partition::~Partition()
1055 {
1056 YSLib::FilterExceptions([this]{
1057 lock_guard<mutex> lck(GetMutexRef());
1058
1059 for(const auto& file : open_files)
1060 // XXX: Flush after exceptions occurred?
1061 TryExpr(file.get().SyncToDisc())
1062 CatchExpr(system_error&, YTraceDe(Err,
1063 "Synchronization of file failed @ Partition::~Partition."))
1064 if(GetFileSystemType() == FileSystemType::FAT32)
1065 WriteFSInfo();
1066 }, yfsig, Warning);
1067 }
1068
1069 void
1070 Partition::CheckPositionForNextCluster(FilePosition& pos) ythrow(system_error)
1071 {
1072 const auto s(pos.GetSector());
1073 const auto spc(GetSectorsPerCluster());
1074
1075 if(YB_UNLIKELY(spc < s))
1076 // XXX: Internal error.
1077 throw_error(errc::invalid_argument);
1078
1079 const auto c(pos.GetCluster());
1080
1081 if(YB_UNLIKELY(!Table.IsFreeOrValid(c)))
1082 // XXX: Internal error.
1083 throw_error(errc::invalid_argument);
1084 if(s == spc)
1085 {
1086 auto t(Table.QueryNext(c));
1087
1088 pos = {Clusters::IsFreeOrEOF(t)
1089 ? Table.LinkFree(pos.GetCluster()) : t, 0, pos.GetByte()};
1090 }
1091 }
1092
1093 void
1094 Partition::ChangeDir(string_view path) ythrow(system_error)
1095 {
1096 DEntry(*this, path, LeafAction::EnsureDirectory, {}, cwd_cluster);
1097 }
1098
1099 void
1100 Partition::CreateFSInfo()
1101 {
1102 YAssert(GetFileSystemType() == FileSystemType::FAT32,
1103 "Only FAT32 supported.");
1104 if(!read_only)
1105 {
1106 // TODO: Use aligned allocation.
1107 auto p_sector_buf(make_unique<byte[]>(GetBytesPerSector()));
1108 const auto sec_buf(p_sector_buf.get());
1109
1110 Table.WriteFSInfo(sec_buf);
1111 if(YB_UNLIKELY(!disc.WriteSectors(fs_info_sector, 1, sec_buf)))
1112 YTraceDe(Err,
1113 "Writing sectors failed on creating file system information.");
1114 }
1115 }
1116
1117 bool
1118 Partition::EntryExists(string_view comp, ClusterIndex dclus)
1119 ythrow(system_error)
1120 {
1121 if(comp.length() < LFN::MaxMBCSLength)
1122 {
1123 DEntry entry(dclus);
1124
1125 while(entry.QueryNextFrom(*this))
1126 if(CompareEntryNames(entry.Data, comp, entry.GetName()))
1127 return true;
1128 }
1129 return {};
1130 }
1131
1132 void
1133 Partition::ExtendPosition(DEntryPosition& entry_pos) ythrow(system_error)
1134 {
1135 switch(IncrementPosition(entry_pos))
1136 {
1137 case ExtensionResult::NoSpace:
1138 throw_error(errc::no_space_on_device);
1139 case ExtensionResult::EndOfFile:
1140 entry_pos.SetCluster(
1141 Table.LinkFreeCleared(entry_pos.GetCluster()));
1142 default:
1143 break;
1144 }
1145 }
1146
1147 ::sec_t
1148 Partition::FindFirstValidPartition(byte* sec_buf) const ynothrowv
1149 {
1150 if(ReadSector(0, sec_buf))
1151 {
1152 byte part_table[16 * 4];
1153 byte* ptr(part_table);
1154
1155 // TODO: Resolve magic numbers.
1156 ystdex::trivially_copy_n(sec_buf + 0x1BE, 16 * 4, part_table);
1157 for(size_t i(0); i < 4; yunseq(++i, ptr += 16))
1158 {
1159 ::sec_t part_lba = read_uint_le<32>(ptr + 0x8);
1160
1161 if(MatchFATSig(sec_buf + FAT16::BS_FilSysType)
1162 || MatchFATSig(sec_buf + FAT32::BS_FilSysType))
1163 return part_lba;
1164 if(ptr[4] == 0)
1165 continue;
1166 if(ptr[4] == 0x0F)
1167 {
1168 ::sec_t part_lba2(part_lba), next_lba2(0);
1169
1170 // NOTE: Up to 8 logic partitions.
1171 for(size_t n = 0; n < 8; ++n)
1172 {
1173 if(!ReadSector(part_lba + next_lba2, sec_buf))
1174 return 0;
1175 // TODO: Resolve magic numbers.
1176 part_lba2 = part_lba + next_lba2
1177 + read_uint_le<32>(sec_buf + 0x1C6);
1178 next_lba2 = read_uint_le<32>(sec_buf + 0x1D6);
1179 if(!ReadSector(part_lba2, sec_buf))
1180 return 0;
1181 if(MatchFATSig(sec_buf + FAT16::BS_FilSysType)
1182 || MatchFATSig(sec_buf + FAT32::BS_FilSysType))
1183 return part_lba2;
1184 if(next_lba2 == 0)
1185 break;
1186 }
1187 }
1188 else
1189 {
1190 if(ReadSector(part_lba, sec_buf))
1191 return 0;
1192 if(MatchFATSig(sec_buf + FAT16::BS_FilSysType)
1193 || MatchFATSig(sec_buf + FAT32::BS_FilSysType))
1194 return part_lba;
1195 }
1196 }
1197 }
1198 return 0;
1199 }
1200
1201 ExtensionResult
1202 Partition::IncrementPosition(DEntryPosition& entry_pos) ynothrowv
1203 {
1204 // FIXME: Wrong assertion on writing.
1205 YAssert(Table.IsFreeOrValid(entry_pos.GetCluster()),
1206 "Invalid allocated cluster found.");
1207
1208 auto pos(entry_pos);
1209
1210 if(pos.IncOffset() == GetBytesPerSector() / EntryDataSize)
1211 {
1212 pos.IncSectorAndResetOffset();
1213
1214 const auto s(pos.GetSector());
1215 const bool root(pos.IsFAT16RootCluster());
1216
1217 if(!root && s == GetSectorsPerCluster())
1218 {
1219 pos.SetSector(0);
1220
1221 const auto c(Table.QueryNext(pos.GetCluster()));
1222
1223 // NOTE: Cover 'c == Clusters::EndOfFile'.
1224 if(!Table.IsValid(c))
1225 return ExtensionResult::EndOfFile;
1226 pos.SetCluster(c);
1227 }
1228 else if(root && s == Table.GetRootDirSectorsNum())
1229 return ExtensionResult::NoSpace;
1230 }
1231 entry_pos = pos;
1232 return ExtensionResult::Success;
1233 }
1234
1235 void
1236 Partition::MakeDir(string_view path) ythrow(system_error)
1237 {
1238 ClusterIndex parent_clus, dclus;
1239
1240 DEntry(*this, path, LeafAction::Return,
1241 [&](DEntry& de){
1242 de.Data.Clear();
1243 de.Data.SetDirectoryAttribute();
1244 de.Data.WriteDateTime();
1245 dclus = Table.LinkFreeCleared(Clusters::Free);
1246 de.Data.WriteCluster(dclus);
1247 // XXX: Only %ENOSPC may be expected.
1248 }, parent_clus);
1249
1250 // NOTE: Create the dot entry within the directory.
1251 EntryData edata;
1252 auto& cache(GetCacheRef());
1253 const auto dir_sector(Table.ClusterToSector(dclus));
1254
1255 edata.SetupRoot(dclus);
1256 // XXX: POSIX does not require %EIO here.
1257 CheckThrowENOSPC(cache.EraseWritePartialSector(dir_sector, 0,
1258 edata.data(), EntryDataSize));
1259 // NOTE: Create the double dot entry within the directory.
1260 edata.SetDot(EntryData::Name + 1);
1261 // NOTE: if parent directory is root then ".." always link to
1262 // Cluster 0.
1263 edata.WriteCluster(parent_clus == GetRootDirCluster()
1264 ? ClusterIndex(Clusters::FAT16RootDirectory) : parent_clus);
1265 CheckThrowENOSPC(cache.WritePartialSector(dir_sector, EntryDataSize,
1266 edata.data(), EntryDataSize));
1267 Flush();
1268 }
1269
1270 ClusterIndex
1271 Partition::ReadClusterFromEntry(const EntryData& data) const
1272 ythrow(system_error)
1273 {
1274 // NOTE: Only high 16 bits of start cluster are used when they are certainly
1275 // correctly defined.
1276 const auto dst(data.data());
1277 ClusterIndex c(read_uint_le<16>(dst + EntryData::Cluster));
1278
1279 c = GetFileSystemType() == FileSystemType::FAT32
1280 ? c | (read_uint_le<16>(dst + EntryData::ClusterHigh) << 16) : c;
1281 CheckThrowEIO(Table.IsFreeOrValid(c));
1282 return c;
1283 }
1284
1285 void
1286 Partition::ReadFSInfo()
1287 {
1288 YAssert(GetFileSystemType() == FileSystemType::FAT32,
1289 "Only FAT32 supported.");
1290
1291 // TODO: Use aligned allocation.
1292 auto p_sector_buf(make_unique<byte[]>(GetBytesPerSector()));
1293 const auto sec_buf(p_sector_buf.get());
1294
1295 if(ReadFSInfoSector(sec_buf))
1296 {
1297 if(read_uint_le<32>(sec_buf + FSI_LeadSig) != FSI_LeadSig_Value
1298 || read_uint_le<32>(sec_buf + FSI_StrucSig) != FSI_StrucSig_Value
1299 || read_uint_le<32>(sec_buf + FSI_Free_Count) == 0)
1300 CreateFSInfo();
1301 else
1302 Table.ReadClusters(sec_buf);
1303 }
1304 }
1305
1306 void
1307 Partition::Rename(string_view old, string_view new_name) ythrow(system_error)
1308 {
1309 YAssertNonnull(new_name.data());
1310 if(!new_name.empty())
1311 {
1312 if(FetchPartitionFromPath(new_name.data()) == this)
1313 {
1314 // FIXME: %errc::invalid_argument: The old pathname names an
1315 // ancestor directory of the new pathname.
1316 DEntry old_dir_entry(*this, old);
1317
1318 DEntry(*this, new_name, LeafAction::Return,
1319 [&](DEntry& de) ynothrow{
1320 yunseq(de.Data = old_dir_entry.Data,
1321 de.NamePos = old_dir_entry.NamePos);
1322 // XXX: Only %ENOSPC may be expected?
1323 });
1324 // FIXME: Exception safety.
1325 RemoveEntry(old_dir_entry.NamePos);
1326 }
1327 else
1328 throw_error(errc::cross_device_link);
1329 }
1330 else
1331 throw_error(errc::no_such_file_or_directory);
1332 }
1333
1334 void
1335 Partition::StatFS(struct ::statvfs& st)
1336 {
1337 // NOTE: Special command was given to sync the free cluster.
1338 if(std::memcmp(&st.f_flag, "SCAN", 4) == 0
1339 && GetFileSystemType() == FileSystemType::FAT32)
1340 CreateFSInfo();
1341
1342 const auto free_count(Table.GetFreeClusters());
1343
1344 yunseq(
1345 // NOTE: FAT clusters = POSIX blocks.
1346 st.f_bsize = GetBytesPerCluster(),
1347 st.f_frsize = GetBytesPerCluster(),
1348 st.f_blocks = Table.GetUsedClusters(),
1349 st.f_bfree = free_count,
1350 st.f_bavail = free_count,
1351 // NOTE: Treat requests for info on inodes as clusters.
1352 st.f_files = Table.GetUsedClusters(),
1353 st.f_ffree = free_count,
1354 st.f_favail = free_count,
1355 st.f_fsid = disc.GetType(),
1356 // NOTE: No support for %ST_ISUID and %ST_ISGID.
1357 st.f_flag = ST_NOSUID | (read_only ? ST_RDONLY : 0),
1358 st.f_namemax = LFN::MaxMBCSLength
1359 );
1360 }
1361
1362 void
1363 Partition::RemoveEntry(const DEntry::NamePosition& np) ythrow(system_error)
1364 {
1365 // NOTE: Create an empty directory entry to overwrite the old ones.
1366 auto name_pos(np);
1367 EntryData edata;
1368
1369 ystdex::retry_on_cond([&]() ynothrow{
1370 return !(name_pos[0] == name_pos[1]);
1371 }, [&]{
1372 CheckReadPartialSector(Table, name_pos[0], edata.data());
1373 edata[0] = EntryData::Free;
1374 CheckWritePartialSector(Table, name_pos[0], edata.data());
1375 CheckThrowEIO(
1376 IncrementPosition(name_pos[0]) == ExtensionResult::Success);
1377 });
1378 Flush();
1379 }
1380
1381 void
1382 Partition::StatFromEntry(const EntryData& data, struct ::stat& st) const
1383 ythrow(system_error)
1384 {
1385 const auto dst(data.data());
1386
1387 st.st_ino = ::ino_t(ReadClusterFromEntry(data));
1388 yunseq(
1389 st.st_dev = disc.GetType(),
1390 // NOTE: The file serial number is the start cluster.
1391 st.st_mode = mode_t(data.IsDirectory() ? Mode::Directory : Mode::Regular)
1392 | mode_t(Mode::Read) | (data.IsWritable() ? mode_t(Mode::Write)
1393 : mode_t()),
1394 // NOTE: Always one hard link on a FAT file.
1395 st.st_nlink = 1,
1396 // NOTE: Faked for FAT.
1397 st.st_uid = 1, st.st_gid = 2,
1398 st.st_size = read_uint_le<32>(dst + EntryData::FileSize),
1399 st.st_atime
1400 = ConvertFileTime(read_uint_le<16>(dst + EntryData::ADate), 0),
1401 st.st_spare1 = 0, st.st_mtime = ConvertFileTime(read_uint_le<16>(
1402 dst + EntryData::MDate), read_uint_le<16>(dst + EntryData::MTime)),
1403 st.st_spare2 = 0, st.st_ctime = ConvertFileTime(read_uint_le<16>(
1404 dst + EntryData::CDate), read_uint_le<16>(dst + EntryData::CTime)),
1405 st.st_spare3 = 0, st.st_blksize = GetBytesPerSector(),
1406 st.st_spare4[0] = 0, st.st_spare4[1] = 0
1407 );
1408
1409 const auto bps(GetBytesPerSector());
1410
1411 yunseq(st.st_rdev = st.st_dev, st.st_blocks = (st.st_size + bps - 1) / bps);
1412 }
1413
1414 void
1415 Partition::Unlink(string_view path) ythrow(system_error)
1416 {
1417 if(!read_only)
1418 {
1419 DEntry dentry(*this, path);
1420 const auto c(ReadClusterFromEntry(dentry.Data));
1421
1422 if(dentry.Data.IsDirectory())
1423 {
1424 DEntry dir_contents(c);
1425
1426 while(dir_contents.QueryNextFrom(*this))
1427 if(!dir_contents.IsDot())
1428 throw_error(errc::operation_not_permitted);
1429 }
1430 if(Table.ClearLinks(c))
1431 {
1432 // FIXME: Exception safety.
1433 RemoveEntry(dentry.NamePos);
1434 return;
1435 }
1436 throw_error(errc::io_error);
1437 }
1438 throw_error(errc::read_only_file_system);
1439 }
1440
1441 void
1442 Partition::WriteFSInfo() const
1443 {
1444 YAssert(GetFileSystemType() == FileSystemType::FAT32,
1445 "Only FAT32 supported.");
1446
1447 // TODO: Use aligned allocation.
1448 auto p_sector_buf(make_unique<byte[]>(GetBytesPerSector()));
1449 const auto sec_buf(p_sector_buf.get());
1450
1451 if(!ReadFSInfoSector(sec_buf))
1452 throw std::runtime_error(
1453 "Failed reading first sector on writing file system information.");
1454 // FIXME: Big endian implementations.
1455 if(read_uint_le<32>(sec_buf + FSI_LeadSig) != FSI_LeadSig_Value
1456 || read_uint_le<32>(sec_buf + FSI_StrucSig) != FSI_StrucSig_Value)
1457 throw std::runtime_error("Failed validating FSInfo signature on"
1458 " writing file system information.");
1459 Table.WriteClusters(sec_buf);
1460 if(!WriteSector(fs_info_sector, sec_buf))
1461 throw std::runtime_error(
1462 "Failed writing file system information sector.");
1463 }
1464
1465
1466 Partition*
1467 FetchPartitionFromPath(const char* path) ynothrowv
1468 {
1469 // NOTE: %::chdir or %::setDefaultDevice call is needed before relative
1470 // paths available here.
1471 if(const auto p_devops = ::GetDeviceOpTab(Nonnull(path)))
1472 return static_cast<Partition*>(p_devops->deviceData);
1473 return {};
1474 }
1475
1476
1477 DirState::DirState(Partition& part, string_view path) ythrow(system_error)
1478 ythrow(system_error)
1479 : part_ref(part), start_cluster([&]{
1480 ClusterIndex dclus;
1481
1482 DEntry(part, path, LeafAction::EnsureDirectory, {}, dclus);
1483 return dclus;
1484 }()), current_entry(start_cluster),
1485 valid_entry(current_entry.QueryNextFrom(GetPartitionRef()))
1486 {}
1487
1488 void
1489 DirState::Iterate(char* filename, struct ::stat* filestat) ythrow(system_error)
1490 {
1491 if(valid_entry)
1492 {
1493 auto& part(GetPartitionRef());
1494
1495 // TODO: Optimize.
1496 ystdex::ntctsncpy(Nonnull(filename), current_entry.GetName().c_str(),
1497 std::min<size_t>(current_entry.GetName().length() + 1, NAME_MAX));
1498 if(filestat)
1499 part.StatFromEntry(current_entry.Data, *filestat);
1500 valid_entry = current_entry.QueryNextFrom(part);
1501 }
1502 else
1503 throw_error(errc::no_such_file_or_directory);
1504 }
1505
1506 void
1507 DirState::Reset() ythrow(system_error)
1508 {
1509 current_entry.NamePos = DEntry::GenerateBeforeFirstNamePos(start_cluster);
1510 valid_entry = current_entry.QueryNextFrom(GetPartitionRef());
1511 }
1512
1513
1514 FileInfo::FileInfo(Partition& part, string_view path, int flags)
1515 {
1516 const auto oflags((OpenMode(flags)));
1517
1518 switch(oflags & OpenMode::AccessMode)
1519 {
1520 case OpenMode::ReadWrite:
1521 attr.set(WriteBit);
1522 case OpenMode::Read:
1523 attr.set(ReadBit);
1524 break;
1525 case OpenMode::Write:
1526 attr.set(WriteBit);
1527 break;
1528 default:
1529 throw_error(errc::permission_denied);
1530 }
1531 // NOTE: %O_CREAT when file not exist would be check later.
1532 if((CanWrite() || bool(oflags & OpenMode::Truncate)) && part.IsReadOnly())
1533 throw_error(errc::read_only_file_system);
1534
1535 lock_guard<mutex> lck(part.GetMutexRef());
1536 const auto do_init([&, this](DEntry&& de){
1537 file_size = de.Data.ReadFileSize();
1538 # if false
1539 // NOTE: Allow LARGEFILEs with undefined results. Make sure that the
1540 // file size can fit in the available space.
1541 if(!(flags & OpenMode::LargeFile) && file_size >= (1 << 31))
1542 throw_error(errc::file_too_large);
1543 # endif
1544 // XXX: Extension on file level.
1545 if(CanWrite() && !de.Data.IsWritable())
1546 throw_error(errc::read_only_file_system);
1547 yunseq(part_ptr = &part,
1548 start_cluster = part.ReadClusterFromEntry(de.Data));
1549 if(bool(oflags & OpenMode::Truncate) && CanWrite()
1550 && start_cluster != 0)
1551 {
1552 part.Table.ClearLinks(start_cluster);
1553 yunseq(start_cluster = Clusters::Free, file_size = 0,
1554 attr.set(ModifiedBit));
1555 }
1556 yunseq(name_position = de.NamePos, current_position = 0,
1557 rw_position = {start_cluster});
1558 if(bool(oflags & OpenMode::Append))
1559 {
1560 attr.set(AppendBit);
1561 // Set append pointer to the end of the file
1562 // TODO: Use %FilePosition::Decompose.
1563 append_position = {part.Table.QueryLast(start_cluster), file_size
1564 % part.GetBytesPerCluster() / part.GetBytesPerSector(),
1565 file_size % part.GetBytesPerSector()};
1566 // NOTE: Check if the end of the file is on the end of a cluster.
1567 if(file_size > 0 && file_size % part.GetBytesPerCluster() == 0)
1568 // NOTE: Set flag to allocate a new cluster
1569 append_position = {append_position.GetCluster(),
1570 part.GetSectorsPerCluster(), 0};
1571 }
1572 else
1573 // NOTE: Guarantee all members initialized.
1574 append_position = rw_position;
1575 Deref(part.LockOpenFiles()).insert(*this);
1576 });
1577
1578 DEntry dentry(part, path, (oflags & OpenMode::CreateExclusive)
1579 == OpenMode::CreateExclusive ? LeafAction::ThrowExisted
1580 : LeafAction::Return, bool(oflags & OpenMode::Create)
1581 ? std::function<void(DEntry&)>([this](DEntry& de) ynothrow{
1582 attr.set(ModifiedBit);
1583 de.Data.Clear();
1584 de.Data.WriteCDateTime();
1585 // FIXME: Only %ENOSPC may be expected.
1586 }) : nullptr);
1587
1588 if(dentry.Data.IsDirectory() && CanWrite())
1589 throw_error(errc::is_a_directory);
1590 do_init(std::move(dentry));
1591 }
1592
1593 void
1594 FileInfo::CheckAppend() ynothrow
1595 {
1596 if(CanAppend())
1597 append_position = rw_position;
1598 }
1599
1600 void
1601 FileInfo::Extend() ythrow(system_error)
1602 {
1603 auto& part(GetPartitionRef());
1604 // NOTE: It is assumed that %start_cluster is always valid. This will be
1605 // true when %Extend is called from %write_r.
1606 // TODO: Use %FilePosition::Decompose?
1607 const auto bpc(part.GetBytesPerCluster());
1608 FilePosition file_pos{part.Table.QueryLast(start_cluster),
1609 file_size % bpc / part.GetBytesPerSector(), file_size % bpc};
1610
1611 YAssert(file_size < current_position, "Invalid position found.");
1612
1613 size_t remain(current_position - file_size);
1614
1615 if(remain > 0 && file_size > 0 && file_pos.GetSector() == 0
1616 && file_pos.GetByte() == 0)
1617 file_pos = {part.Table.LinkFree(file_pos.GetCluster()), 0,
1618 file_pos.GetByte()};
1619 if(remain + file_pos.GetByte() < part.GetBytesPerSector())
1620 {
1621 // NOTE: Only need to clear to the end of the sector.
1622 CheckFillPartialSector(part.Table, file_pos, remain);
1623 file_pos.AddByte(remain);
1624 }
1625 else
1626 {
1627 if(file_pos.GetByte() > 0)
1628 {
1629 const auto delta(part.GetBytesPerSector() - file_pos.GetByte());
1630
1631 CheckFillPartialSector(part.Table, file_pos, delta);
1632 remain -= delta;
1633 file_pos.IncSectorAndResetOffset();
1634 }
1635 while(remain >= part.GetBytesPerSector())
1636 {
1637 if(file_pos.GetSector() >= part.GetSectorsPerCluster())
1638 file_pos = {part.Table.LinkFree(file_pos.GetCluster()),
1639 0, file_pos.GetByte()};
1640 CheckFillSectors(part.Table, file_pos);
1641 remain -= part.GetBytesPerSector();
1642 file_pos.IncSector();
1643 }
1644 if(remain != 0)
1645 {
1646 part.CheckPositionForNextCluster(file_pos);
1647 file_pos.SetByte(remain);
1648 CheckFillPartialSectorOff(part.Table, file_pos, 0, remain);
1649 }
1650 }
1651 rw_position = file_pos;
1652 file_size = current_position;
1653 }
1654
1655 ::ssize_t
1656 FileInfo::Read(char* buf, size_t nbyte) ythrow(system_error)
1657 {
1658 auto& part(GetPartitionRef());
1659
1660 if(nbyte != 0 && current_position < file_size
1661 && start_cluster != Clusters::Free)
1662 {
1663 if(file_size < nbyte + current_position)
1664 nbyte = file_size - current_position;
1665
1666 auto remain(nbyte);
1667 auto pos(rw_position);
1668 // NOTE: Align to sector.
1669 auto tmp(std::min<size_t>(part.GetBytesPerSector() - pos.GetByte(),
1670 remain));
1671
1672 if(tmp < part.GetBytesPerSector())
1673 {
1674 CheckReadPartialSector(part.Table, pos, buf, tmp);
1675 yunseq(remain -= tmp, buf += tmp),
1676 pos.AddByte(tmp);
1677 if(pos.GetByte() >= part.GetBytesPerSector())
1678 pos.IncSectorAndResetOffset();
1679 }
1680 // NPTE: Align to cluster, %tmp is number of sectors to read.
1681 tmp = remain > (part.GetSectorsPerCluster() - pos.GetSector())
1682 * part.GetBytesPerSector() ? part.GetSectorsPerCluster()
1683 - pos.GetSector() : remain / part.GetBytesPerSector();
1684 if(tmp > 0)
1685 {
1686 CheckReadSectors(part.Table, pos, buf, tmp);
1687
1688 const auto n(tmp * part.GetBytesPerSector());
1689
1690 yunseq(buf += n, remain -= n),
1691 pos.AddSector(tmp);
1692 }
1693 // NOTE: Move onto next cluster. It should get to here without reading
1694 // anything if a cluster is due to be allocated.
1695 if(pos.GetSector() >= part.GetSectorsPerCluster())
1696 {
1697 const auto next_start(part.Table.QueryNext(pos.GetCluster()));
1698
1699 if(remain == 0 && next_start == Clusters::EndOfFile)
1700 pos.SetSector(part.GetSectorsPerCluster());
1701 else
1702 AssignValidCluster(part, pos, next_start);
1703 }
1704 // NOTE: Read in whole clusters, contiguous blocks at a time.
1705 while(remain >= part.GetBytesPerCluster())
1706 {
1707 size_t chunk_size(0);
1708 ClusterIndex next_start(pos.GetCluster()), chunk_end(
1709 ystdex::retry_on_cond([&](ClusterIndex end) ynothrow{
1710 return next_start == end + 1
1711 && chunk_size + part.GetBytesPerCluster() <= remain;
1712 }, [&]() ynothrow{
1713 chunk_size += part.GetBytesPerCluster();
1714 return ystdex::exchange(next_start,
1715 part.Table.QueryNext(next_start));
1716 }));
1717
1718 CheckReadSectors(part.Table, pos.GetCluster(), buf,
1719 chunk_size / part.GetBytesPerSector());
1720 yunseq(buf += chunk_size, remain -= chunk_size);
1721 if(remain == 0 && next_start == Clusters::EndOfFile)
1722 pos = {chunk_end, part.GetSectorsPerCluster(), pos.GetByte()};
1723 else
1724 AssignValidCluster(part, pos, next_start);
1725 }
1726 // NOTE: %tmp is number of sectors left.
1727 tmp = remain / part.GetBytesPerSector();
1728 if(tmp > 0)
1729 {
1730 CheckReadSectors(part.Table, pos.GetCluster(), buf, tmp);
1731
1732 const auto n(tmp * part.GetBytesPerSector());
1733
1734 yunseq(buf += n, remain -= n),
1735 pos.AddSector(tmp);
1736 }
1737 if(remain > 0)
1738 {
1739 CheckReadPartialSectorOff(part.Table, pos, buf, 0, remain);
1740 pos.AddByte(remain);
1741 remain = 0;
1742 }
1743 nbyte -= remain;
1744 yunseq(rw_position = pos, current_position += nbyte);
1745 return nbyte;
1746 }
1747 return 0;
1748 }
1749
1750 ::off_t
1751 FileInfo::Seek(::off_t offset, int whence) ythrow(system_error)
1752 {
1753 ::off_t new_pos(offset);
1754
1755 switch(whence)
1756 {
1757 case SEEK_SET:
1758 break;
1759 case SEEK_CUR:
1760 new_pos += ::off_t(current_position);
1761 break;
1762 case SEEK_END:
1763 new_pos += ::off_t(file_size);
1764 break;
1765 default:
1766 throw_error(errc::invalid_argument);
1767 }
1768
1769 auto& part(GetPartitionRef());
1770
1771 if(!(0 < offset && new_pos < 0))
1772 {
1773 // NOTE: %new_pos can only be larger than the %MaxFileSize on platforms
1774 // where ::off_t is larger than 32 bits.
1775 if(!(new_pos < 0
1776 || (sizeof(new_pos) > 4 && new_pos > ::off_t(MaxFileSize))))
1777 {
1778 auto pos(static_cast<std::uint32_t>(new_pos));
1779
1780 if(pos <= file_size && start_cluster != Clusters::Free)
1781 {
1782 ptrdiff_t clus_count(pos / part.GetBytesPerCluster());
1783 ClusterIndex cluster(start_cluster);
1784
1785 if(pos >= current_position)
1786 {
1787 ptrdiff_t
1788 cur_count(current_position / part.GetBytesPerCluster());
1789 if(rw_position.GetSector() == part.GetSectorsPerCluster())
1790 --cur_count;
1791 yunseq(clus_count -= cur_count,
1792 cluster = rw_position.GetCluster());
1793 }
1794 rw_position.Decompose(pos, part.GetBytesPerCluster(),
1795 part.GetBytesPerSector());
1796 for(auto next_cluster(part.Table.QueryNext(cluster));
1797 clus_count > 0 && part.Table.IsValid(next_cluster);
1798 next_cluster = part.Table.QueryNext(cluster))
1799 {
1800 --clus_count;
1801 cluster = next_cluster;
1802 }
1803 if(clus_count > 0)
1804 {
1805 if(clus_count == 1 && file_size == pos
1806 && rw_position.GetSector() == 0)
1807 rw_position = {rw_position.GetCluster(),
1808 part.GetSectorsPerCluster(), 0};
1809 else
1810 throw_error(errc::invalid_argument);
1811 }
1812 rw_position.SetCluster(cluster);
1813 }
1814 current_position = pos;
1815 return pos;
1816 }
1817 throw_error(errc::invalid_argument);
1818 }
1819 throw_error(errc::value_too_large);
1820 }
1821
1822 void
1823 FileInfo::Stat(struct ::stat& st) const
1824 {
1825 auto& part(GetPartitionRef());
1826 EntryData edata;
1827
1828 CheckReadPartialSector(part.Table, name_position[1], edata.data());
1829 part.StatFromEntry(edata, st);
1830 // NOTE: Update fields that have changed since the file was opened.
1831 yunseq(st.st_ino = ::ino_t(start_cluster), st.st_size = file_size);
1832 }
1833
1834 void
1835 FileInfo::SyncToDisc() ythrow(system_error)
1836 {
1837 if(CanWrite() && IsModified())
1838 {
1839 auto& part(GetPartitionRef());
1840 EntryData dir_entry_data;
1841 const auto data(dir_entry_data.data());
1842 const auto& dir_entry_end(GetNamePosition()[1]);
1843 const auto c(GetStartCluster());
1844 const auto date_time(FetchDateTime());
1845
1846 CheckReadPartialSector(part.Table, dir_entry_end, data);
1847 write_uint_le<32>(data + EntryData::FileSize, GetSize()),
1848 dir_entry_data.WriteCluster(c),
1849 write_uint_le<16>(data + EntryData::MTime, date_time.second),
1850 write_uint_le<16>(data + EntryData::MDate, date_time.first),
1851 write_uint_le<16>(data + EntryData::ADate, date_time.first);
1852 data[EntryData::Attributes] |= EntryDataUnit(Attribute::Archive);
1853 CheckWritePartialSector(part.Table, dir_entry_end, data);
1854 part.GetCacheRef().Flush();
1855 }
1856 attr.set(ModifiedBit, {});
1857 }
1858
1859 void
1860 FileInfo::Truncate(FileSize new_size) ythrow(system_error)
1861 {
1862 auto& part(GetPartitionRef());
1863
1864 if(new_size > GetSize())
1865 {
1866 if(start_cluster == Clusters::Free)
1867 {
1868 ClusterIndex t(part.Table.LinkFree(Clusters::Free));
1869
1870 yunseq(start_cluster = t, rw_position = {t});
1871 }
1872
1873 const FilePosition saved_position(GetRWPosition());
1874 const FileSize saved_offset(GetCurrentPosition());
1875
1876 SetCurrentPosition(new_size);
1877 Extend();
1878 CheckAppend();
1879 SetRWPosition(saved_position),
1880 SetCurrentPosition(saved_offset);
1881 }
1882 else if(new_size < GetSize())
1883 {
1884 if(new_size == 0)
1885 {
1886 part.Table.ClearLinks(GetStartCluster());
1887 yunseq(start_cluster = Clusters::Free,
1888 append_position = {Clusters::Free});
1889 }
1890 else
1891 {
1892 // NOTE: Trim the file down to the required size. Drop the unneeded
1893 // end of the cluster chain. If the end falls on a cluster
1894 // boundary, drop that cluster too, then set a flag to allocate
1895 // a cluster as needed.
1896 const auto last_cluster(part.Table.TrimChain(GetStartCluster(),
1897 (new_size - 1) / part.GetBytesPerCluster()));
1898
1899 if(CanAppend())
1900 {
1901 const auto re(new_size % part.GetBytesPerCluster());
1902
1903 SetAppendPosition({last_cluster, re == 0
1904 ? part.GetSectorsPerCluster()
1905 : re / part.GetBytesPerSector(),
1906 new_size % part.GetBytesPerSector()});
1907 }
1908 }
1909 }
1910 UpdateSize(new_size);
1911 }
1912
1913 ::ssize_t
1914 FileInfo::Write(const char* buf, size_t nbyte) ythrow(system_error)
1915 {
1916 // FIXME: Only write up to the maximum file size, taking wrap-around of
1917 // integers into account. Overflow?
1918 if(nbyte + file_size > MaxFileSize || nbyte + file_size < file_size)
1919 nbyte = MaxFileSize - file_size;
1920 if(nbyte != 0)
1921 {
1922 auto& part(GetPartitionRef());
1923 size_t remain(nbyte);
1924
1925 if(start_cluster == Clusters::Free)
1926 {
1927 start_cluster = part.Table.LinkFree(Clusters::Free);
1928 yunseq(append_position = {start_cluster},
1929 rw_position = {start_cluster});
1930 }
1931
1932 bool flag_appending(CanAppend());
1933 auto pos(flag_appending ? append_position : rw_position);
1934
1935 if(!flag_appending)
1936 {
1937 if(current_position > file_size)
1938 Extend();
1939 if(nbyte + current_position > file_size)
1940 flag_appending = true;
1941 }
1942 if(remain != 0)
1943 part.CheckPositionForNextCluster(pos);
1944
1945 // NOTE: Align to sector.
1946 size_t tmp(std::min<size_t>(part.GetBytesPerSector() - pos.GetByte(),
1947 remain));
1948
1949 if(tmp < part.GetBytesPerSector())
1950 {
1951 CheckWritePartialSector(part.Table, pos, buf, tmp);
1952 yunseq(remain -= tmp, buf += tmp);
1953 pos.AddByte(tmp);
1954 if(pos.GetByte() >= part.GetBytesPerSector())
1955 pos.IncSectorAndResetOffset();
1956 }
1957 // NOTE: Align to sector. %tmp is number of sectors to write.
1958 tmp = remain > (part.GetSectorsPerCluster() - pos.GetSector())
1959 * part.GetBytesPerSector() ? part.GetSectorsPerCluster()
1960 - pos.GetSector() : remain / part.GetBytesPerSector();
1961 if(tmp > 0 && tmp < part.GetSectorsPerCluster())
1962 {
1963 CheckWriteSectors(part.Table, pos, buf, tmp);
1964
1965 const auto n(tmp * part.GetBytesPerSector());
1966
1967 yunseq(buf += n, remain -= n),
1968 pos.AddSector(tmp);
1969 }
1970 while(remain >= part.GetBytesPerCluster())
1971 {
1972 if(remain != 0)
1973 part.CheckPositionForNextCluster(pos);
1974
1975 auto chunk_end(pos.GetCluster()), next_start(chunk_end);
1976 size_t chunk_size(part.GetBytesPerCluster());
1977 FilePosition next_position(pos);
1978
1979 // NOTE: Group consecutive clusters.
1980 while(chunk_size + part.GetBytesPerCluster() < remain)
1981 {
1982 // NOTE: Pretend to use up all sectors in %next_position.
1983 next_position.SetSector(part.GetSectorsPerCluster());
1984 // TODO: != ?
1985 if(remain > chunk_size)
1986 part.CheckPositionForNextCluster(next_position);
1987 next_start = next_position.GetCluster();
1988 if(next_start != chunk_end + 1)
1989 break;
1990 yunseq(chunk_end = next_start,
1991 chunk_size += part.GetBytesPerCluster());
1992 }
1993 CheckWriteSectors(part.Table, pos.GetCluster(), buf,
1994 chunk_size / part.GetBytesPerSector());
1995 yunseq(buf += chunk_size, remain -= chunk_size);
1996 if(chunk_end != next_start && part.Table.IsValid(next_start))
1997 // NOTE: New cluster is already allocated (because it was not
1998 // consecutive).
1999 pos = {next_start, 0, pos.GetByte()};
2000 else
2001 pos = {chunk_end, part.GetSectorsPerCluster(), pos.GetByte()};
2002 }
2003 if(remain != 0)
2004 part.CheckPositionForNextCluster(pos);
2005 // NOTE: %tmp is number of sectors left.
2006 tmp = remain / part.GetBytesPerSector();
2007 if(tmp > 0)
2008 {
2009 CheckWriteSectors(part.Table, pos.GetCluster(), buf, tmp);
2010
2011 const auto n(tmp * part.GetBytesPerSector());
2012
2013 yunseq(buf += n, remain -= n),
2014 pos.AddSector(tmp);
2015 }
2016 if(remain > 0)
2017 {
2018 if(flag_appending)
2019 CheckThrowEIO(part.GetCacheRef().EraseWritePartialSector(
2020 PosToSec(part.Table, pos), 0, buf, remain));
2021 else
2022 CheckWritePartialSectorOff(part.Table, pos, buf, 0, remain);
2023 pos.AddByte(remain);
2024 remain = 0;
2025 }
2026 yunseq(nbyte -= remain, attr.set(ModifiedBit));
2027 if(CanAppend())
2028 {
2029 append_position = pos;
2030 file_size += nbyte;
2031 }
2032 else
2033 {
2034 rw_position = pos;
2035 current_position += nbyte;
2036 if(file_size < current_position)
2037 file_size = current_position;
2038 }
2039 }
2040 return nbyte;
2041 }
2042
2044 namespace
2045 {
2046
2047 template<typename _func, typename... _tParams>
2048 using FilterRes = ystdex::common_nonvoid_t<ystdex::result_of_t<
2049 _func(_tParams&&...)>, int>;
2050
2051 inline PDefH(int, seterr, int& re, int e) ynothrow
2052 ImplRet(re = e, -1)
2053 inline YB_NONNULL(1) PDefH(int, seterr, ::_reent* r, int e) ynothrow
2054 // NOTE: The newlib reentrant environment pointer shall not be null in a
2055 // %::devoptab_t operation.
2056 ImplRet(Deref(r)._errno = e, -1)
2057
2058 template<typename _func>
2059 auto
2060 FilterDevOps(::_reent* r, _func f) ynothrowv -> FilterRes<_func>
2061 {
2062 using fresult = decltype(f());
2063 using result = ystdex::common_nonvoid_t<fresult, int>;
2064 static yconstexpr const auto
2065 def_val(std::is_pointer<fresult>::value ? result() : result(-1));
2066
2067 TryRet(ystdex::call_for_value(0, f))
2068 CatchExpr(std::system_error& e, seterr(r, e.code().value()))
2069 CatchExpr(std::bad_alloc&, seterr(r, ENOMEM))
2070 CatchExpr(..., YAssert(false, "Invalid exception found."))
2071 return def_val;
2072 }
2073
2075 template<typename _fCallable>
2076 YB_NONNULL(1) auto
2077 op_path(::_reent* r, const char*& path, _fCallable f) ynothrowv
2078 -> FilterRes<_fCallable, Partition&>
2079 {
2080 return FilterDevOps(r, [=, &path]{
2081 if(Deref(path) != char())
2082 {
2083 if(const auto p_part = FetchPartitionFromPath(path))
2084 {
2085 // XXX: %EINVAL is not a POSIX error for path.
2086 path = CheckColons(path);
2087 return ystdex::invoke(f, *p_part);
2088 }
2089 // XXX: This is not a POSIX error.
2090 throw_error(errc::no_such_device);
2091 }
2092 throw_error(errc::no_such_file_or_directory);
2093 });
2094 }
2095
2097
2098 template<class _tHolder, typename _fCallable, class _type, typename... _tParams>
2099 inline auto
2100 locked_invoke(_tHolder& holder, _fCallable&& f, _type& x, _tParams&&... args)
2101 -> decltype(ystdex::invoke(yforward(f), x, yforward(args)...))
2102 {
2103 lock_guard<mutex> lck(holder.GetMutexRef());
2104
2105 return ystdex::invoke(yforward(f), x, yforward(args)...);
2106 }
2107
2108 template<typename _fCallable, typename... _tParams>
2109 YB_NONNULL(1) auto
2110 op_path_locked(::_reent* r, const char*& path, _fCallable f, _tParams&&... args)
2111 ynothrowv -> FilterRes<_fCallable, Partition&, _tParams&&...>
2112 {
2113 return op_path(r, path, [&, f](Partition& part){
2114 // XXX: Blocked. 'yforward' cause G++ 5.3 crash: internal compiler
2115 // error: Segmentation fault.
2116 return locked_invoke(part, f, part, std::forward<_tParams&&>(args)...);
2117 });
2118 }
2119
2120 template<typename _fCallable, typename... _tParams>
2121 YB_NONNULL(1) auto
2122 op_dir_locked(::_reent* r, ::DIR_ITER* dir_state, _fCallable f,
2123 _tParams&&... args) ynothrowv
2124 -> FilterRes<_fCallable, DirState&, _tParams&&...>
2125 {
2126 auto& state(Deref(static_cast<DirState*>(Deref(dir_state).dirStruct)));
2127
2128 return FilterDevOps(r, [&, f]{
2129 // XXX: Extended partition mutex to lock directory states.
2130 // XXX: Blocked. 'yforward' cause G++ 5.3 crash: internal compiler
2131 // error: Segmentation fault.
2132 return locked_invoke(state.GetPartitionRef(), f, state,
2133 std::forward<_tParams&&>(args)...);
2134 });
2135 }
2136
2137 template<typename _fCheck, typename _fCallable, typename... _tParams>
2138 YB_NONNULL(1) auto
2139 op_file_checked(::_reent* r, int fd, _fCheck check, _fCallable f,
2140 _tParams&&... args) ynothrowv
2141 -> FilterRes<_fCallable, FileInfo&, _tParams&&...>
2142 {
2143 auto& file(Deref(reinterpret_cast<FileInfo*>(fd)));
2144
2145 return FilterDevOps(r, [&, fd, check, f]{
2146 // NOTE: Check of %fd is similar to %::close_r.
2147 if(ystdex::invoke(check, file))
2148 // XXX: Blocked. 'yforward' cause G++ 5.3 crash: internal compiler
2149 // error: Segmentation fault.
2150 return locked_invoke(file, f, file,
2151 std::forward<_tParams&&>(args)...);
2152 throw_error(errc::bad_file_descriptor);
2153 });
2154 }
2155
2156 template<typename _fCallable, typename... _tParams>
2157 YB_NONNULL(1) auto
2158 op_file_locked(::_reent* r, int fd, _fCallable f, _tParams&&... args)
2159 ynothrowv -> FilterRes<_fCallable, FileInfo&, _tParams&&...>
2160 {
2161 auto& file(Deref(reinterpret_cast<FileInfo*>(fd)));
2162
2163 return FilterDevOps(r, [&, fd, f]{
2164 // XXX: Blocked. 'yforward' cause G++ 5.3 crash: internal compiler
2165 // error: Segmentation fault.
2166 return locked_invoke(file, f, file, std::forward<_tParams&&>(args)...);
2167 });
2168 }
2170
2171 // NOTE: %REENTRANT_SYSCALLS_PROVIDED is configured for libgloss for
2172 // arm-*-*-eabi targets in devkitPro ports. See source
2173 // 'newlib/libgloss/configure'.
2174
2175 const ::devoptab_t dotab_fat{
2176 "fat", sizeof(FileInfo), [](::_reent* r, void* file_struct,
2177 const char* path, int flags, int) YB_NONNULL(1, 2) ynothrowv{
2178 // NOTE: Before call of %::devoptab_t::open_r, the parameter
2179 // %file_struct is set as a pointer after handling in implementation
2180 // of devkitPro port of newlib (libsysbase) %::_open_r, which is
2181 // assumed to be not null.
2182 return op_path(r, path, [=, &path](Partition& part){
2183 ::new(Nonnull(file_struct)) FileInfo(part, path, flags);
2184
2185 return int(file_struct);
2186 });
2187 }, [](::_reent* r, int fd) YB_NONNULL(1) ynothrowv{
2188 // NOTE: The parameter %fd is actually cast from the file structure
2189 // pointer stored by %devoptab_t::open_r. This function is called
2190 // when the reference count in handle decreased to zero. Since this
2191 // pointer would not be null for non-predefined streams unless
2192 // corrupted, check for null (yield %EBADF) is not performed, as same
2193 // as libfat did. Also the name is %fd, not POSIX %filedes.
2194 return FilterDevOps(r, [=]{
2195 auto& file_info(Deref(reinterpret_cast<FileInfo*>(fd)));
2196 auto& part(file_info.GetPartitionRef());
2197 const auto gd(ystdex::make_guard([&]() ynothrow{
2198 Deref(part.LockOpenFiles()).erase(file_info);
2199 file_info.~FileInfo();
2200 }));
2201 lock_guard<mutex> lck(part.GetMutexRef());
2202
2203 file_info.SyncToDisc();
2204 });
2205 }, [](::_reent* r, int fd, const char* buf, size_t nbyte) YB_NONNULL(2, 4)
2206 ynothrowv{
2207 return op_file_checked(r, fd, &FileInfo::CanWrite, &FileInfo::Write,
2208 buf, nbyte);
2209 }, [](::_reent* r, int fd, char* buf, size_t nbyte) YB_NONNULL(2, 4)
2210 ynothrowv{
2211 return op_file_checked(r, fd, &FileInfo::CanRead, &FileInfo::Read, buf,
2212 nbyte);
2213 }, [](::_reent* r, int fd, ::off_t offset, int whence) YB_NONNULL(1)
2214 ynothrowv{
2215 return op_file_locked(r, fd, &FileInfo::Seek, offset, whence);
2216 }, [](::_reent* r, int fd, struct ::stat* buf) YB_NONNULL(2, 4)
2217 ynothrowv{
2218 return op_file_locked(r, fd, &FileInfo::Stat, Deref(buf));
2219 }, [](::_reent* r, const char* path, struct ::stat* buf) YB_NONNULL(1, 2, 3)
2220 ynothrowv{
2221 return op_path_locked(r, path, &Partition::Stat, Deref(buf), path);
2222 }, [](::_reent* r, const char*, const char*) YB_NONNULL(1, 2, 3) ynothrowv{
2223 return seterr(r, ENOTSUP);
2224 }, [](::_reent* r, const char* path) YB_NONNULL(1, 2) ynothrowv{
2225 return op_path_locked(r, path, &Partition::Unlink, path);
2226 }, [](::_reent* r, const char* path) YB_NONNULL(1, 2) ynothrowv{
2227 return op_path_locked(r, path, &Partition::ChangeDir, path);
2228 }, [](::_reent* r, const char* old, const char* new_name)
2229 YB_NONNULL(1, 2, 3) ynothrowv{
2230 return op_path_locked(r, old, &Partition::Rename, old, new_name);
2231 }, [](::_reent* r, const char* path, int) YB_NONNULL(1, 2) ynothrowv{
2232 return op_path_locked(r, path, &Partition::MakeDir, path);
2233 }, sizeof(DirState), [](::_reent* r, ::DIR_ITER* dir_state,
2234 const char* path) YB_NONNULL(1, 2, 3) ynothrowv{
2235 return op_path_locked(r, path, [=, &path](Partition& part)
2236 -> ::DIR_ITER*{
2237 const auto p(dir_state->dirStruct);
2238
2239 ::new(Nonnull(p)) DirState(part, path);
2240 return static_cast<::DIR_ITER*>(p);
2241 });
2242 }, [](::_reent* r, ::DIR_ITER* dir_state) YB_NONNULL(1, 2) ynothrowv{
2243 return op_dir_locked(r, dir_state, &DirState::Reset);
2244 }, [](::_reent* r, ::DIR_ITER* dir_state, char* filename,
2245 struct ::stat* filestat) YB_NONNULL(1, 2, 3) ynothrowv{
2246 // NOTE: The filename is of %NAME_MAX characters in newlib DS port.
2247 return
2248 op_dir_locked(r, dir_state, &DirState::Iterate, filename, filestat);
2249 }, [](::_reent* r, ::DIR_ITER* dir_state) YB_NONNULL(1, 2) ynothrowv{
2250 return op_dir_locked(r, dir_state, [](DirState& state) ynothrow{
2251 state.~DirState();
2252 });
2253 }, [](::_reent* r, const char* path, struct ::statvfs* buf)
2254 YB_NONNULL(1, 2, 3) ynothrowv{
2255 return op_path_locked(r, path, &Partition::StatFS, Deref(buf));
2256 }, [](::_reent* r, int fd, ::off_t length) YB_NONNULL(1) ynothrowv -> int{
2257 return length >= 0 ? (sizeof(length) <= 4 || length <= ::off_t(
2258 MaxFileSize) ? op_file_checked(r, fd, &FileInfo::CanWrite,
2259 &FileInfo::Truncate, std::uint32_t(length)) : EFBIG)
2260 : EINVAL;
2261 }, [](::_reent* r, int fd) YB_NONNULL(1) ynothrowv -> int{
2262 return op_file_locked(r, fd, &FileInfo::SyncToDisc);
2263 }, nullptr, nullptr, nullptr, nullptr
2264 };
2265
2266 } // unnamed namespace;
2267
2268 bool
2269 Mount(string_view name, const ::DISC_INTERFACE& intf, ::sec_t start_sector,
2270 size_t pages, size_t sectors_per_page_shift)
2271 {
2272 YAssertNonnull(name.data());
2273 if(intf.startup() && intf.isInserted())
2274 {
2275 const auto devname(name.to_string() + ':');
2276
2277 if(::FindDevice(devname.c_str()) >= 0)
2278 return true;
2279 if(auto p = unique_raw(static_cast<::devoptab_t*>(::operator new(
2280 sizeof(::devoptab_t) + name.length() + 1)),
2281 static_cast<void(&)(void*)>(::operator delete)))
2282 if(auto p_part = make_unique<Partition>(intf, pages,
2283 sectors_per_page_shift, start_sector))
2284 {
2285 const auto
2286 p_name(ystdex::aligned_store_cast<char*>(p.get() + 1));
2287
2288 yunseq(ystdex::trivially_copy_n(&dotab_fat, 1, p.get()),
2289 ystdex::ntctscpy(p_name, name.data(), name.length()),
2290 p->name = p_name, p->deviceData = p_part.get());
2291 if(YB_UNLIKELY(::AddDevice(p.get())) == -1)
2292 throw std::runtime_error("Adding device failed.");
2293 p_part.release();
2294 p.release();
2295 return true;
2296 }
2297 YTraceDe(Alert, "Memory allocation failure @ FATMount.");
2298 }
2299 return {};
2300 }
2301
2302 bool
2303 Unmount(const char* name) ynothrow
2304 {
2305 if(const auto p_devops = ::GetDeviceOpTab(Nonnull(name)))
2306 if(p_devops->open_r == dotab_fat.open_r && ::RemoveDevice(name) != -1)
2307 {
2308 delete static_cast<Partition*>(p_devops->deviceData);
2309 ::operator delete(const_cast<::devoptab_t*>(p_devops));
2310 return true;
2311 }
2312 return {};
2313 }
2314
2315 }
2316 #endif
2317
2318 } // namespace platform_ex;
2319
platform::FAT::FAT32::BPB_ExtFlags
Definition: YCLib/FileSystem.h:761
YTraceDe
#define YTraceDe(...)
YCLib 默认调试跟踪。
Definition: Debug.h:313
ystdex::ntctscpy
enable_if_irreplaceable_char_t< _tChar, _tChar * > ntctscpy(_tChar *s1, const _tChar *s2)
复制 NTCTS 。
Definition: cstring.h:250
platform::FAT::BPB_RootEntCnt
Definition: YCLib/FileSystem.h:710
platform::FAT::BPB_RsvdSecCnt
Definition: YCLib/FileSystem.h:708
YSLib::IO::Path::EnsureDirectory
EnsureDirectory
Definition: YSLib/Service/FileSystem.h:508
platform::FAT::LFN::EntryLength
Definition: YCLib/FileSystem.h:945
ystdex::cpp2014::result_of_t
typename result_of< _type >::type result_of_t
Definition: type_traits.hpp:355
platform::FAT::FAT32::BPB_RootClus
Definition: YCLib/FileSystem.h:763
ystdex::block_buffer::write
void write(size_t offset, const void *src, size_t n)
写入外部字节序列的内容到缓冲区。
Definition: cstdio.cpp:193
ystdex::block_buffer::get
byte * get() const
Definition: ystdex/cstdio.h:311
NPL::dst
YF_API const char src const string const char * dst
Definition: Dependency.h:95
platform::st_nlink
return st st_nlink
Definition: FileIO.cpp:1186
YSLib::IO::Path::YB_NONNULL
PDefH(String, GetExtensionOf, const Path &pth) ImplRet(pth.empty()?String() YF_API YB_NONNULL(1) bool VerifyDirectory(const char *)
判断路径表示绝对路径。
Definition: YSLib/Service/FileSystem.h:466
platform::Descriptions::Alert
Definition: YCommon.h:164
platform::FAT::LFN::Reserved2
Definition: YCLib/FileSystem.h:922
platform::FAT::LFN::OffsetTable
yconstexpr const size_t OffsetTable[]
组成长文件名中的 UCS-2 字符在项中的偏移量表。
Definition: YCLib/FileSystem.h:928
platform::FAT::LFN::MaxMBCSLength
UTF-8 项最大长度。
Definition: YCLib/FileSystem.h:943
platform::FAT::LFN::ValidateName
YF_API bool ValidateName(string_view) ynothrowv
检查名称是否为合法的可被 UCS-2 表示的非控制字符组成。
Definition: YCLib/FileSystem.cpp:590
platform
默认平台命名空间。
Definition: Container.h:47
platform::HDirectory::PDefH
#define PDefH(_t, _n,...)
已知文件系统类型。
Definition: YCLib/FileSystem.h:670
ystdex::block_buffer::fill
void fill(size_t offset, size_t n=1, byte value={})
填充字节到缓冲区。
Definition: cstdio.cpp:176
platform::Deref
yconstfn auto Deref(_type &&p) -> decltype(*p)
断言并解引用非空指针。
Definition: Debug.h:451
ystdex::trivially_copy_n
_type * trivially_copy_n(const _type *first, size_t n, _type *result)
复制不覆盖的序列。
Definition: algorithm.hpp:394
platform::size_t
YF_API size_t
Definition: FileIO.h:1353
NPL::Decompose
list< string > Decompose(string_view src)
Definition: Lexical.cpp:379
yfsig
#define yfsig
展开为表示函数签名的预定义变量的宏。
Definition: ydef.h:728
YSLib::Drawing::r
The boundary is out of the const Graphics const Rect const Point const Point Color c const Rect const Rect & r
Definition: YDraw.h:174
ystdex::trivially_fill_n
_type * trivially_fill_n(_type *first, size_t n=1, byte value={})
填充字节序列。
Definition: algorithm.hpp:369
YSLib::IO::Path::ImplRet
#define ImplRet(...)
验证目录路径结尾：确保当且仅当验证成功时保留结尾字符。
Definition: YSLib/Service/FileSystem.h:479
CHRLib::f
f(d, s)
YSLib::IO::EnsureDirectory
void EnsureDirectory(const Path &pth)
Definition: YSLib/Service/FileSystem.cpp:106
platform::FAT::BPB_FATSz16
Definition: YCLib/FileSystem.h:713
platform::FAT::LFN::LastLongEntry
Ordinal 中标记结束的项。
Definition: YCLib/FileSystem.h:960
YAssertNonnull
#define YAssertNonnull(_expr)
Definition: cassert.h:120
YB_UNLIKELY
#define YB_UNLIKELY(_expr)
分支预测提示。
Definition: ydef.h:464
NPL::c_str
YF_API const char src c_str())) YF_API void InstallDirectory(const string &
安装目录：复制目录树。
Definition: Dependency.h:131
ystdex::ntctslen
size_t ntctslen(const _tChar *s)
计算简单 NTCTS 长度。
Definition: cstring.h:456
platform::FAT::BPB_SecPerClus
Definition: YCLib/FileSystem.h:707
ystdex::cpp2014::make_unique
enable_if_t<!is_array< _type >::value, std::unique_ptr< _type > > make_unique(_tParams &&...args)
使用 new 和指定参数构造指定类型的 std::unique_ptr 对象。
Definition: memory.hpp:852
YSLib::TryInvoke
ystdex::nonvoid_result_t< ystdex::result_of_t< _fCallable &&(_tParams &&...)> > TryInvoke(_fCallable &&f, _tParams &&...args) ynothrow
调用函数并尝试返回。
Definition: YException.h:186
TryRet
#define TryRet(...)
指定表达式在 try 块中求值并返回。
Definition: YBaseMacro.h:124
CHRLib::args
_tParams && args
Definition: CharacterProcessing.h:538
yconstexpr
#define yconstexpr
指定编译时常量表达式。
Definition: ydef.h:679
CatchIgnore
#define CatchIgnore(_ex)
指定异常处理器忽略指定异常。
Definition: YBaseMacro.h:133
YSLib::Text::buf
const char * buf
Definition: TextFile.h:81
ystdex::unique_raw
std::unique_ptr< _type > unique_raw(_pSrc *p)
Definition: memory.hpp:740
ystdex::data
auto data(_tRange &c) -> decltype(c.data())
Definition: range.hpp:280
ystdex::max
const _type & max(const _type &a, const _type &b, _fComp comp=less< _type >())
取较大的元素。
Definition: algorithm.hpp:549
ystdex::block_buffer::read
void read(void *dst, size_t offset, size_t n) const
读取缓冲区内容到外部存储。
Definition: cstdio.cpp:185
ystdex::cpp2011::end
_type * end(_type(&&array)[_vN])
Definition: range.hpp:54
platform::FAT::FAT16::BS_FilSysType
Definition: YCLib/FileSystem.h:742
yunseq
#define yunseq
无序列依赖表达式组求值。
Definition: ydef.h:1022
platform::FAT::FileSize
std::uint32_t FileSize
文件大小：字节数。
Definition: YCLib/FileSystem.h:870
platform::FAT::FSI_Free_Count
Definition: YCLib/FileSystem.h:793
YSLib::Drawing::TextState::x
const TextState &ts const TextState &ts const TextState &ts TextState SPos x
Definition: TextBase.h:190
platform::FAT::LFN::IllegalCharacters
const char IllegalCharacters[]
非法长文件名字符。
Definition: YCLib/FileSystem.h:964
platform::FAT::BPB_TotSec32
Definition: YCLib/FileSystem.h:717
TryExpr
#define TryExpr(...)
指定表达式在 try 块中求值。
Definition: YBaseMacro.h:120
ystdex::throw_error
void throw_error(std::error_code ec, _tParams &&...args)
抛出错误： std::system_error 或允许相同构造函数参数的异常。
Definition: ystdex/exception.h:206
platform::FAT::BPB_TotSec16
Definition: YCLib/FileSystem.h:711
platform::FAT::MaxFileSize
static yconstexpr const auto MaxFileSize(FileSize(0xFFFFFFFF))
最大文件大小。
YAssert
#define YAssert(_expr, _msg)
Definition: cassert.h:111
platform::FAT::LFN::Ordinal
长文件名序数。
Definition: YCLib/FileSystem.h:903
CatchExpr
#define CatchExpr(_ex,...)
指定表达式作为异常处理器。
Definition: YBaseMacro.h:129
platform::FAT::FSI_Nxt_Free
Definition: YCLib/FileSystem.h:794
YSLib::FilterExceptions
bool FilterExceptions(_func f, const char *desc={}, RecordLevel lv=Alert, ExceptionTracer trace=ExtractAndTrace) ynothrow
调用函数并过滤宿主异常。
Definition: YException.h:203
ynothrow
#define ynothrow
YSLib 无异常抛出保证：若支持 noexcept 关键字， 指定特定的 noexcept 异常规范。
Definition: ydef.h:766
ystdex::cache_lookup
auto cache_lookup(_tMap &cache, const _tKey &key, _func init, _tParams &&...args) -> decltype((cache.begin() ->second))
以指定的关键字查找作为缓存的无序关联容器， 若没有找到使用指定的可调用对象和参数初始化内容。 ...
Definition: cache.hpp:369
CHRLib::s
_tDst const _tSrc * s
Definition: CharacterProcessing.h:140
NPL::LexemeCategory::Data
数据字面量。
platform::if
if(!p) p
ystdex::cpp2014::exchange
_type exchange(_type &obj, _type2 &&new_val)
交换值并返回旧值。
Definition: utility.hpp:207
platform::FAT::LFN::ConvertToMBCS
string ConvertToMBCS(const char16_t *path)
Definition: YCLib/FileSystem.cpp:568
YSLib::i
return i
Definition: YCoreUtilities.h:176
platform_ex::Windows::Invalid
Definition: MinGW32.h:471
YModules.h
platform::FAT::FAT16::Signature_word
Definition: YCLib/FileSystem.h:744
CHRLib::MakeUCS2LE
_tDst MakeUCS2LE(basic_string_view< char > sv, _tParams &&...args)
Definition: CharacterProcessing.h:538
ystdex::min
const _type & min(const _type &a, const _type &b, _fComp comp=less< _type >())
取较小的元素。
Definition: algorithm.hpp:527
platform::FAT::FSI_StrucSig_Value
yconstexpr const std::uint32_t FSI_StrucSig_Value(0x61417272)
platform::FAT::FAT16::BS_VolLab
Definition: YCLib/FileSystem.h:741
YModules.h
ystdex::read_uint_le
make_width_int< _vWidth >::unsigned_type read_uint_le(const byte *buf)
从字节缓冲区读取指定宽的小端序无符号整数。
Definition: cstdint.hpp:328
platform::FAT::FetchDateTime
YF_API pair< Timestamp, Timestamp > FetchDateTime() ynothrow
取以实时时钟的文件日期和时间。
Definition: YCLib/FileSystem.cpp:635
platform::FAT::FAT32::BPB_FSInfo
Definition: YCLib/FileSystem.h:764
platform::FAT::FSI_TrailSig
Definition: YCLib/FileSystem.h:796
platform::FAT::FAT32::BPB_FATSz32
Definition: YCLib/FileSystem.h:760
functional.hpp
函数和可调用对象。
YSLib::Drawing::GBinaryGroup::get
yconstfn _type & get(GBinaryGroup< _type > &val)
取分量。
Definition: YGDIBase.h:242
CatchRet
#define CatchRet(_ex,...)
指定返回语句作为异常处理器。
Definition: YBaseMacro.h:137
ystdex::call_for_value
auto call_for_value(_type &&val, _fCallable &&f, _tParams &&...args) -> common_nonvoid_t< result_of_t< _fCallable &&(_tParams &&...)>, _type >
调用第二个参数起指定的函数对象，若返回空类型则使用第一个参数的值为返回值。
Definition: functional.hpp:967
ynothrowv
#define ynothrowv
YSLib 无异常抛出保证验证：有条件地使用无异常抛出规范。
Definition: ydef.h:786
ystdex::ntctsncpy
enable_if_irreplaceable_char_t< _tChar, _tChar * > ntctsncpy(_tChar *s1, const _tChar *s2, size_t n)
复制不超过指定长度的 NTCTS 。
Definition: cstring.h:315
ystdex::rtrim
_tString && rtrim(_tString &&str, typename string_traits< _tString >::value_type c)
删除字符串中指定的连续后缀字符。
Definition: string.hpp:676
CHRLib::e
const char const char * e
Definition: CharacterProcessing.h:109
platform_ex::Reset
unsigned long Reset(COMPtr< _iCOM > &ptr) ynothrow
Definition: COM.h:406
platform::FAT::BPB_NumFATs
Definition: YCLib/FileSystem.h:709
platform::FAT::CheckColons
const char * CheckColons(const char *path)
Definition: YCLib/FileSystem.cpp:849
std
Definition: memory.hpp:1183
ythrow
#define ythrow(...)
YSLib 动态异常规范：根据是否使用异常规范宏指定或忽略动态异常规范。
Definition: ydef.h:817
ystdex::byte
unsigned char byte
字节类型。
Definition: ydef.h:834
platform::Nonnull
_type && Nonnull(_type &&p) ynothrowv
断言并返回非空参数。
Definition: Debug.h:422
platform::FAT::LFN::MaxLength
UCS-2 项最大长度。
Definition: YCLib/FileSystem.h:937
platform::FAT::LFN::Flag
Definition: YCLib/FileSystem.h:910
platform::Descriptions::Warning
Definition: YCommon.h:167
platform::Descriptions::Err
Definition: YCommon.h:166
YModules.h
scope_guard.hpp
作用域守护。
platform::FAT::FSI_TrailSig_Value
yconstexpr const std::uint32_t FSI_TrailSig_Value(0xAA550000)
platform_ex
平台扩展命名空间。
Definition: Android.h:45
YSLib::UI::name
yconstfn const string & name
Definition: Loader.h:130
platform::FAT::LFN::Reserved1
Definition: YCLib/FileSystem.h:912
platform::FAT::ClusterIndex
std::uint32_t ClusterIndex
Definition: YCLib/FileSystem.h:695
platform::FAT::BPB_BytsPerSec
Definition: YCLib/FileSystem.h:706
CHRLib::c
c
Definition: CharacterProcessing.h:81
yforward
#define yforward(_expr)
根据参数类型使用 std::forward 传递对应参数。
Definition: ydef.h:995
ystdex::retry_on_cond
result_of_t< _fCallable &&(_tParams &&...)> retry_on_cond(_fCond cond, _fCallable &&f, _tParams &&...args)
循环重复调用：代替直接使用 do-while 语句以避免过多引入作用域外的变量。
Definition: functional.hpp:1152
ystdex::invoke
result_of_t< _fCallable &&(_tParams &&...)> invoke(_fCallable &&f, _tParams &&...args)
调用可调用对象。
Definition: functional.hpp:339
platform_ex::Windows::Archive
Definition: MinGW32.h:453
CHRLib::char16_t
char16_t((*c_ptr<< CHAR_BIT)|c_ptr[1])) inline PDefH(char16_t
取 c_ptr 指向的小端序双字节字符。
ystdex::write_uint_le
void write_uint_le(byte *buf, typename make_width_int< _vWidth >::unsigned_type val)
向字节缓冲区写入指定宽的小端序无符号整数。
Definition: cstdint.hpp:352
CHRLib::d
_tDst * d
Definition: CharacterProcessing.h:139
ystdex::ntctsicmp
int ntctsicmp(const _tChar *s1, const _tChar *s2)
按字典序比较简单 NTCTS （忽略大小写）。
Definition: cstring.h:213
platform::FAT::ConvertFileTime
YF_API std::time_t ConvertFileTime(Timestamp, Timestamp) ynothrow
转换日期和时间的时间戳为标准库时间类型。
Definition: YCLib/FileSystem.cpp:618
ystdex::size
auto size(const _tRange &c) -> decltype(c.size())
类容器访问。
Definition: range.hpp:233
ystdex::make_unique_default_init
enable_if_t<!is_array< _type >::value, std::unique_ptr< _type > > make_unique_default_init()
Definition: memory.hpp:879
platform::FAT::EntryDataUnit
std::uint8_t EntryDataUnit
Definition: YCLib/FileSystem.h:694
YSLib::IO::int
YF_API int
Definition: File.h:51
platform::FAT::LFN::GenerateAliasChecksum
EntryDataUnit GenerateAliasChecksum(const EntryDataUnit *p) ynothrowv
Definition: YCLib/FileSystem.cpp:576
platform::FAT::FSI_LeadSig
Definition: YCLib/FileSystem.h:790
platform::ssize_t
ystdex::make_signed_t< std::size_t > ssize_t
Definition: NativeAPI.h:94
platform::FAT::FSI_LeadSig_Value
yconstexpr const std::uint32_t FSI_LeadSig_Value(0x41615252)
ystdex::ref
lref< _type > ref(_type &t)
构造引用包装。
Definition: ref.hpp:132
YSLib::path
YF_API const char * path
Definition: Initialization.h:77
platform::FAT::MaxSectorSize
yconstexpr const size_t MaxSectorSize(4096)
ystdex::ltrim
_tString && ltrim(_tString &&str, typename string_traits< _tString >::value_type c)
删除字符串中指定的连续前缀字符。
Definition: string.hpp:645
platform::FAT::EntryDataSize
yconstexpr const size_t EntryDataSize(0x20)
目录项数据大小。
platform::mode_t
::mode_t mode_t
文件模式类型。
Definition: FileIO.h:114
ystdex::block_buffer
块缓冲。
Definition: ystdex/cstdio.h:262
platform::FAT::LFN::CheckSum
短文件名（别名）校验和。
Definition: YCLib/FileSystem.h:914
ystdex::string_view
basic_string_view< char > string_view
Definition: string_view.hpp:415
YSLib::Drawing::Padding::src
src
Definition: YGDI.h:454
CHRLib::p
const auto p(d)
platform::FAT::FSI_StrucSig
Definition: YCLib/FileSystem.h:792
platform_ex::Windows::Directory
Definition: MinGW32.h:452
ystdex::common_nonvoid_t
cond_or_t< is_void< _type >, _type, common_type_t, _types... > common_nonvoid_t
取公共非空类型：若第一参数为非空类型则为第一参数，否则从其余参数推断。
Definition: type_op.hpp:332