YSTest: DSIO.cpp 源文件

YSTest PreAlpha_b700_20160611 The YSLib Test Project

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

- 文件列表
- 文件成员

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