|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2023 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // Windrv (相当) ホストドライバ
9: //
10:
11: #include "hostwindrv.h"
1.1.1.4 root 12: #include "ascii_ctype.h"
1.1.1.5 ! root 13: #include "mytime.h"
1.1 root 14: #include <dirent.h>
15: #include <inttypes.h>
16: #include <fcntl.h>
17: #include <sys/types.h>
18: #include <sys/stat.h>
19: #if defined(HAVE_STATVFS)
20: #include <sys/statvfs.h>
21: #elif defined(HAVE_STATFS)
22: #include <sys/statfs.h>
23: #endif
24: #include <sys/time.h>
25: #include <algorithm>
26:
27: // コンストラクタ
28: HostWindrv::HostWindrv(WindrvDevice *windrv)
29: : inherited(OBJ_HOSTWINDRV)
30: {
31: // ホストデバイスのログレベルは常に VM デバイス(親)に従属なので
32: // ドライバオブジェクトにログエイリアスは不要。
33: ClearAlias();
34:
35: // コンストラクト後ただちにログ出力できるようここで一度追従しておく。
36: // 以降はホストデバイスの SetLogLevel() で変更する。
37: loglevel = windrv->loglevel;
38: }
39:
40: // デストラクタ
41: HostWindrv::~HostWindrv()
42: {
43: }
44:
45: // ルートパスの設定。
1.1.1.2 root 46: bool
1.1 root 47: HostWindrv::InitRootPath(const std::string& rootpath_)
48: {
49: rootpath = rootpath_;
50:
51: // ルートパスのほうは末尾の '/' なし。
52: // ゲストパスが必ず '/' から始まるため。
1.1.1.3 root 53: while (rootpath.empty() == false && rootpath.back() == '/') {
1.1 root 54: rootpath.pop_back();
55: }
56: if (rootpath.empty()) {
57: rootpath = ".";
58: }
1.1.1.2 root 59:
60: struct stat st;
61: int r = stat(rootpath.c_str(), &st);
62: if (r < 0) {
63: putmsg(1, "stat '%s': %s", rootpath.c_str(), strerror(errno));
64: return false;
65: }
66: if (!S_ISDIR(st.st_mode)) {
67: putmsg(1, "stat '%s': Not directory", rootpath.c_str());
68: return false;
69: }
70: r = access(rootpath.c_str(), R_OK | X_OK);
71: if (r != 0) {
72: putmsg(1, "access '%s': %s", rootpath.c_str(), strerror(errno));
73: return false;
74: }
75:
76: return true;
1.1 root 77: }
78:
79: // 初期化コマンド。
80: // 戻り値はステータスコード。
81: uint32
82: HostWindrv::Initialize()
83: {
84: vdirs.clear();
85:
86: return 0;
87: }
88:
89: // ゲストパスのディレクトリ path に対応する VDir をオープンして返す。
90: // 見付からなければ NULL を返す。
91: // path はゲストパスなので "/BIN/" を指定して、マッチしたホストディレクトリ
92: // (および VDir の検索キー) が "/bin/" とかはある。
93: VDir *
94: HostWindrv::OpenVDir(const std::string& path)
95: {
96: putmsg(2, "%s: '%s'", __func__, path.c_str());
97: std::vector<std::string> search_names = SplitPath(path);
98:
99: std::string cwd = "/";
100: VDir *vdir;
101: for (auto it = search_names.begin();;) {
102: // cwd に対する dir を取得。
103: vdir = FindVDir(cwd);
104: if (vdir) {
105: // 見付かれば更新。
106: UpdateVDir(vdir);
107: } else {
108: // キャッシュになければ、ホストのディレクトリを読んで作成。
109: putmsg(2, "%s: add '%s'", __func__, cwd.c_str());
110: vdir = AddVDir(cwd);
111: if (vdir == NULL) {
112: // それでもなければ、ない。
113: putmsg(2, "%s: vdir not found", __func__);
114: return NULL;
115: }
116: }
117:
118: // サブディレクトリへ降りる必要がなければここまで。
119: if (it == search_names.end()) {
120: break;
121: }
122:
123: const auto& name = *it;
124: const VDirent *ent = vdir->MatchName(name, true);
125: if (ent == NULL) {
126: putmsg(2, "%s: '%s' not found", __func__, name.c_str());
127: return NULL;
128: }
129: if (ent->IsDir() == false) {
130: putmsg(2, "%s: '%s' is not dir", __func__, name.c_str());
131: return NULL;
132: }
133: // サブディレクトリが見付かったので、ここに降りて次へ。
134: ++it;
135: cwd += name;
136: cwd += '/';
137: }
138:
139: vdir->Acquire();
140: return vdir;
141: }
142:
143: // ホスト相対パス path に対応する VDir を返す。
144: // 見付からなければ NULL を返す。
145: // この時点では VDir のタイムスタンプは更新しない。
146: VDir *
147: HostWindrv::FindVDir(const std::string& path) const
148: {
149: std::string nmpath = NormalizePath(path);
150: auto it = vdirs.find(nmpath);
151: if (it == vdirs.end()) {
152: return NULL;
153: }
154: return it->second;
155: }
156:
157: // 仮想ディレクトリ dir を必要なら更新。
158: bool
159: HostWindrv::UpdateVDir(VDir *vdir)
160: {
161: return FillVDir(vdir, __func__, false);
162: }
163:
164: // ホスト相対パス path に対応する VDir を作成して追加。
165: VDir *
166: HostWindrv::AddVDir(const std::string& path)
167: {
168: std::string nmpath = NormalizePath(path);
169:
170: VDir *new_vdir = new VDir(this, nmpath);
171: if (FillVDir(new_vdir, __func__, true) == false) {
172: delete new_vdir;
173: return NULL;
174: }
175:
176: // vdirs に追加。
177: // この時点で存在しないはず。
178: assert(vdirs.find(nmpath) == vdirs.end());
179:
180: // 一杯なら、参照した時間がもっとも古いものを削除。
181: // 本当は Refcount がゼロなやつでないとおかしいはずだが、
182: // そんなに使用中のはずはないのでいいか…。
183: if (vdirs.size() >= MaxVDirs) {
184: auto it = std::min_element(vdirs.begin(), vdirs.end(),
185: [](const auto& a, const auto& b) {
186: const auto av = a.second;
187: const auto bv = b.second;
188: if (av->Refcount() < bv->Refcount())
189: return true;
190: if (av->Refcount() > bv->Refcount())
191: return false;
192: return av->GetATime() < bv->GetATime();
193: }
194: );
195: auto old_vdir = it->second;
196: putmsg(2, "%s Purge '%s'", __func__, old_vdir->GetPath().c_str());
197: delete old_vdir;
198: vdirs.erase(it);
199: }
200: vdirs[nmpath] = new_vdir;
201: return new_vdir;
202: }
203:
204: // vdir のディレクトリエントリを(再)構成する。
205: // caller は呼び出し元関数名。ログ表示のため。
206: // force が true なら常に作成、false なら更新があったときだけ作成。
207: bool
208: HostWindrv::FillVDir(VDir *vdir, const char *caller, bool force)
209: {
210: struct stat st;
211:
212: std::string path = vdir->GetPath();
213: std::string fullpath = rootpath + path;
214: int r = stat(fullpath.c_str(), &st);
215: if (r < 0) {
216: if (errno == ENOENT) {
217: putmsg(2, "%s: '%s' (%s) not found", caller,
218: path.c_str(), fullpath.c_str());
219: } else {
220: putmsg(2, "%s: '%s' (%s): %s", caller,
221: path.c_str(), fullpath.c_str(), strerror(errno));
222: }
223: return false;
224: }
225:
226: #if defined(HAVE_STAT_ST_TIMESPEC)
227: // 更新判定は st_mtimespec がある環境でだけ行う。
228: // st_mtime だと同一の1秒内に更新したことが分からないため。(Linux)
229: if (force == false) {
230: // OS のディレクトリが VDir より新しいか調べる。
231: if (vdir->GetMTime() == timespec2nsec(st.st_mtimespec)) {
232: return true;
233: }
234: putmsg(2, "%s: '%s' (%s) has been updated", caller,
235: path.c_str(), fullpath.c_str());
236: }
237: #endif
238:
239: auto& list = vdir->list;
240: list.clear();
241:
242: // ホストのディレクトリエントリから仮想ディレクトリエントリを作成。
243: DIR *dir = opendir(fullpath.c_str());
244: if (dir == NULL) {
245: putmsg(2, "%s: opendir '%s' (%s): %s", caller,
246: path.c_str(), fullpath.c_str(), strerror(errno));
247: return false;
248: }
249: for (struct dirent *d; (d = readdir(dir)) != NULL; ) {
250: // VDirent の属性はディレクトリかファイルか、のみ。
251: bool isdir;
252: if (d->d_type == DT_DIR) {
253: isdir = true;
254: } else if (d->d_type == DT_REG) {
255: isdir = false;
256: } else if (d->d_type == DT_LNK) {
257: // リンク先を調べる
258: std::string linkname = fullpath + std::string(d->d_name);
259: struct stat linkst;
260: r = stat(linkname.c_str(), &linkst);
261: if (r < 0) {
262: continue;
263: }
264: if (S_ISDIR(linkst.st_mode)) {
265: isdir = true;
266: } else if (S_ISREG(linkst.st_mode)) {
267: isdir = false;
268: } else {
269: continue;
270: }
271: } else {
272: continue;
273: }
274:
275: if (IsHuman68kFilename(d->d_name) == false) {
276: continue;
277: }
278: list.emplace_back(std::string(d->d_name), isdir);
279: }
280: closedir(dir);
281:
282: // ファイル名をチェック。
283: if (Windrv::option_case_sensitive == false) {
284: // 大文字小文字を区別しないモードでは "a.bak" と "a.BAK" は
285: // 同一なので、どちらもゲストに出現させない。
286: for (int i = 0, iend = list.size() - 1; i < iend; i++) {
287: if (list[i].IsValid() == false) {
288: continue;
289: }
290: for (int j = i + 1, jend = list.size(); j < jend; j++) {
291: if (strcasecmp(list[i].name.c_str(),
292: list[j].name.c_str()) == 0)
293: {
294: list[i].Invalidate();
295: list[j].Invalidate();
296: }
297: }
298: }
299: }
300:
301: if (loglevel >= 3) {
302: putmsgn("%s: Enumerating '%s'...", caller, path.c_str());
303: for (int i = 0; i < list.size(); i++) {
304: putmsgn(" %c%c%c '%s'",
305: (list[i].IsValid() ? '-' : 'I'),
306: (list[i].IsArchive() ? 'A' : '-'),
307: (list[i].IsDir() ? 'D' : '-'),
308: list[i].name.c_str());
309: }
310: }
311:
312: // タイムスタンプを更新。
313: #if defined(HAVE_STAT_ST_TIMESPEC)
314: vdir->SetMTime(timespec2nsec(st.st_mtimespec));
315: #endif
316: vdir->UpdateATime();
317:
318: return true;
319: }
320:
321: // パスを分解して返す。
322: // "/a/bc/" なら { "a", "bc" } に。
323: // "/" なら { } を返す。
324: /*static*/ std::vector<std::string>
325: HostWindrv::SplitPath(const std::string& path)
326: {
327: // 先頭と末尾の '/' を取り除く。
328: int s = 0;
329: int e = path.size();
330: for (; s < e; s++) {
331: if (path[s] != '/') {
332: break;
333: }
334: }
335: for (e--; e >= s; e--) {
336: if (path[e] != '/') {
337: break;
338: }
339: }
340: std::string tmppath = path.substr(s, e - s + 1);
341:
342: // '/' で分解。
343: // path が "/a/bc/" なら tmppath は "a/bc" になっているので
344: // これで { "a", "bc" } になる。
345: std::vector<std::string> sep = string_split(tmppath, '/');
346: return sep;
347: }
348:
349: // 相対パス文字列を正規化する。
350: // vdirs[] のキーに使う場合用。
351: /*static*/ std::string
352: HostWindrv::NormalizePath(const std::string& path)
353: {
354: std::string src = "/" + path + "/";
355: std::string dst;
356:
357: bool sep = false;
358: for (auto c : src) {
359: if (sep) {
360: if (c != '/') {
361: sep = false;
362: dst += c;
363: }
364: } else {
365: if (c == '/') {
366: sep = true;
367: }
368: dst += c;
369: }
370: }
371: return dst;
372: }
373:
374: // vdir をクローズする。
375: void
376: HostWindrv::CloseVDir(VDir *vdir)
377: {
378: if (vdir) {
379: vdir->Release();
380: }
381: }
382:
383: // fname が Human68k で使えるファイル名なら true を返す。
384: bool
385: HostWindrv::IsHuman68kFilename(const char *fname) const
386: {
387: if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) {
388: return true;
389: }
390:
391: const std::string name = fname; //ToGuestCharset(fname);
392:
393: for (auto c : name) {
394: // TODO: 記号どこまで使えるか調べる
1.1.1.4 root 395: if (!is_ascii_alnum(c) && strchr("-._", c) == NULL) {
1.1 root 396: return false;
397: }
398: }
399:
400: const char *p = strchr(name.c_str(), '.');
401: if (p == NULL) {
402: // '.' がない場合は 8+10 文字以内でなければいけない。
403: if (name.size() > 18) {
404: return false;
405: }
406: } else {
407: const char *e = strrchr(name.c_str(), '.');
408: if (p == e) {
409: // '.' が1つなら、1.1 文字以上 8+10.3 文字以内でなければいけない。
410: int len1 = p - name.c_str();
411: int len2 = strlen(p + 1);
412: if (len1 < 1 || len1 > 18) {
413: return false;
414: }
415: if (len2 < 1 || len2 > 3) {
416: return false;
417: }
418: } else {
419: // '.' が複数は未対応にするか。
420: return false;
421: }
422: }
423:
424: return true;
425: }
426:
427: // ゲスト文字コードの文字列をホスト文字コードに変換。
428: std::string
429: HostWindrv::ToHostCharset(const std::string& src) const
430: {
431: // XXX Not supported yet
432: return src;
433: }
434:
435: // ホスト文字コードの文字列をゲスト文字コードに変換。
436: std::string
437: HostWindrv::ToGuestCharset(const std::string& src) const
438: {
439: // XXX Not supported yet
440: return src;
441: }
442:
443: // メディア容量を取得する。
444: // 成功すれば cap にパラメータを書き出して 0 を返す。
445: // 失敗すればステータスコードを返す。
446: uint32
447: HostWindrv::GetCapacity(Human68k::capacity *cap)
448: {
449: uint64 totalbytes;
450: uint64 availbytes;
451: uint bytes_per_sector;
452: uint sectors_per_cluster;
453: uint total_clusters;
454: uint avail_clusters;
455:
456: // メディアの総容量と空き容量を取得。
457: #if defined(HAVE_STATVFS)
458: struct statvfs buf;
459: int r = statvfs(rootpath.c_str(), &buf);
460: if (r < 0) {
461: putmsg(1, "statvfs: %s: %s", rootpath.c_str(), strerror(errno));
462: return Human68k::RES_INVALID_PARAM;
463: }
464: #elif defined(HAVE_STATFS)
465: struct statfs buf;
466: int r = statfs(rootpath.c_str(), &buf);
467: if (r < 0) {
468: putmsg(1, "statfs: %s: %s", rootpath.c_str(), strerror(errno));
469: return Human68k::RES_INVALID_PARAM;
470: }
471: #else
472: putmsg(0, "%s: No statfs() nor statvfs()", __func__);
473: return Human68k::RES_INVALID_PARAM;
474: #endif
475: totalbytes = (uint64)buf.f_bsize * (uint64)buf.f_blocks;
476: availbytes = (uint64)buf.f_bsize * (uint64)buf.f_bavail;
477:
478: // 空き容量/総容量ともに 2GB に制限する。
479: if (availbytes >= 0x8000'0000U) {
480: availbytes = 0x7fff'ffff;
481: }
482: if (totalbytes >= 0x8000'0000U) {
483: totalbytes = 0x8000'0000U;
484: }
485: // クラスタを適当にでっちあげる。
486: bytes_per_sector = 512;
487: sectors_per_cluster = 128;
488: total_clusters = totalbytes / (sectors_per_cluster * bytes_per_sector);
489: avail_clusters = availbytes / (sectors_per_cluster * bytes_per_sector);
490:
491: cap->avail_clusters = avail_clusters;
492: cap->total_clusters = total_clusters;
493: cap->sectors_per_cluster = sectors_per_cluster;
494: cap->bytes_per_sector = bytes_per_sector;
495:
496: return 0;
497: }
498:
499: // ホストパス path の示すファイルの Human68k 属性を返す。
500: // エラーの場合はステータスコードを返す。
501: // stp があれば stat(2) の結果を書き戻す。
502: uint32
503: HostWindrv::GetAttributeInternal(const std::string& fullpath,
504: struct stat *stp) const
505: {
506: struct stat stbuf;
507: uint32 attr;
508: int r;
509:
510: if (stp == NULL) {
511: stp = &stbuf;
512: }
513:
514: r = stat(fullpath.c_str(), stp);
515: if (r < 0) {
516: putmsg(1, "%s: stat: %s: %s",
517: __func__, fullpath.c_str(), strerror(errno));
518: return Human68k::RES_INVALID_PARAM; // 何返すのがいいか
519: }
520:
521: // 属性。
522: if (S_ISDIR(stp->st_mode)) {
523: attr = Human68k::ATTR_DIR;
524: } else if (S_ISREG(stp->st_mode)) {
525: attr = Human68k::ATTR_ARCHIVE;
526: } else {
527: putmsg(1, "%s: st_mode is %o", __func__, stp->st_mode);
528: return Human68k::RES_INVALID_PARAM; // 何返すのがいいか
529: }
530:
531: // 自分に書き込み権がないものは全部 ReadOnly。
532: r = access(fullpath.c_str(), W_OK);
533: if (r < 0) {
534: if (errno == EACCES || errno == EROFS) {
535: attr |= Human68k::ATTR_RDONLY;
536: } else {
537: putmsg(1, "%s: access: %s: %s",
538: __func__, fullpath.c_str(), strerror(errno));
539: return Human68k::RES_INVALID_PARAM; // 何返すのがいいか
540: }
541: }
542:
543: return attr;
544: }
545:
546: // path の示すファイルについて FILES の所定のフィールドを埋めて返す。
547: bool
548: HostWindrv::GetFileStat(Human68k::FILES *files, const std::string& path) const
549: {
550: struct stat st;
551:
552: assert(files);
553:
554: std::string fullpath = rootpath + path;
555: uint32 attr = GetAttributeInternal(fullpath, &st);
556: if ((int32)attr < 0) {
557: return false;
558: }
559: files->attr = attr;
560:
561: uint32 datetime = Human68k::Unixtime2DateTime(st.st_mtime);
562: files->date = datetime >> 16;
563: files->time = datetime & 0xffff;
564:
565: // 2GB を超えるファイルは扱わないことにする。
566: if (st.st_size >= 0x8000'0000) {
567: putmsg(1, "%s: %s: size exceeds", __func__, fullpath.c_str());
568: return false;
569: }
570: files->size = (uint32)st.st_size;
571:
572: return true;
573: }
574:
575: // ホスト相対パス path で示されるディレクトリを作成する。
576: uint32
577: HostWindrv::MakeDir(const std::string& path)
578: {
579: std::string fullpath = rootpath + path;
580: int r = mkdir(fullpath.c_str(), 0755);
581: if (r < 0) {
582: putmsg(1, "%s: mkdir %s: %s", __func__,
583: fullpath.c_str(), strerror(errno));
584: return -1; // ?
585: }
586: return 0;
587: }
588:
589: // ホスト相対パス path で示されるディレクトリを削除する。
590: // 削除するディレクトリは空でなければならない。
591: uint32
592: HostWindrv::RemoveDir(const std::string& path)
593: {
594: std::string fullname = rootpath + path;
595: int r = rmdir(fullname.c_str());
596: if (r < 0) {
597: putmsg(1, "%s: rmdir %s: %s", __func__,
598: fullname.c_str(), strerror(errno));
599: if (errno == ENOTEMPTY) {
600: return Human68k::RES_CANNOT_DELETE;
601: }
602: return -1; // ?
603: }
604: return 0;
605: }
606:
607: // ホスト相対パス oldpath を newpath にリネームもしくは移動する。
608: uint32
609: HostWindrv::Rename(const std::string& oldpath, const std::string& newpath)
610: {
611: std::string oldfullpath = rootpath + oldpath;
612: std::string newfullpath = rootpath + newpath;
613: int r = rename(oldfullpath.c_str(), newfullpath.c_str());
614: if (r < 0) {
615: putmsg(1, "%s: rename %s %s: %s", __func__,
616: oldfullpath.c_str(), newfullpath.c_str(), strerror(errno));
617: return -1; // ?
618: }
619: return 0;
620: }
621:
622: // ホスト相対パス path で示されるファイルを削除する。
623: // ディレクトリは削除出来ない。
624: uint32
625: HostWindrv::Delete(const std::string& path)
626: {
627: std::string fullname = rootpath + path;
628: int r = unlink(fullname.c_str());
629: if (r < 0) {
630: putmsg(1, "%s: unlink %s: %s", __func__,
631: fullname.c_str(), strerror(errno));
632: return -1; // ?
633: }
634: return 0;
635: }
636:
637: // ホスト相対パス path で示されるファイルの属性を返す。
638: // 属性は Human68k::ATTR_*。
639: // エラーの場合はステータスコードを返す。
640: uint32
641: HostWindrv::GetAttribute(const std::string& path)
642: {
643: std::string fullname = rootpath + path;
644: uint32 result = GetAttributeInternal(fullname, NULL);
645: return result;
646: }
647:
648: // ホスト相対パス path で示されるファイルの属性を attr に変更する。
649: // attr は Human68k::ATTR_*。
650: // 現状 RDONLY の上げ下げのみ対応。
651: uint32
652: HostWindrv::SetAttribute(const std::string& path, uint8 newattr)
653: {
654: struct stat st;
655:
656: std::string fullname = rootpath + path;
657: uint32 attr = GetAttributeInternal(fullname, &st);
658:
659: attr &= Human68k::ATTR_RDONLY;
660: newattr &= Human68k::ATTR_RDONLY;
661:
662: mode_t newmode;
663: if (attr == 0 && newattr != 0) {
664: newmode = st.st_mode & ~0200;
665: } else if (attr != 0 && newattr == 0) {
666: newmode = st.st_mode | 0200;
667: } else {
668: return 0;
669: }
670:
671: int r = chmod(fullname.c_str(), newmode);
672: if (r < 0) {
673: putmsg(1, "%s: chmod(%03o) %s: %s", __func__,
674: newmode & 0777, fullname.c_str(), strerror(errno));
675: return -1; // ?
676: }
677: return 0;
678: }
679:
680: // ホスト相対パス path で示されるファイルを作成する。
681: // 属性 attr は今の所 RDONLY しか見ていない。
682: uint32
683: HostWindrv::CreateFile(Windrv::FCB *fcb, const std::string& path, uint8 attr)
684: {
685: assert(fcb);
686:
687: int mode = 0666;
688: if ((attr & Human68k::ATTR_RDONLY)) {
689: mode &= ~0222;
690: }
691:
692: std::string fullname = rootpath + path;
693: fcb->fd = open(fullname.c_str(), O_CREAT | O_TRUNC | O_WRONLY, mode);
694: if (fcb->fd < 0) {
695: putmsg(1, "%s: create %s: %s", __func__,
696: fullname.c_str(), strerror(errno));
697: return Human68k::RES_INVALID_FUNC; // ?
698: }
699:
700: return 0;
701: }
702:
703: // ホスト相対パス path で示されるファイルをオープンする。
704: // fcb->mode は Human68k::OPEN_* のいずれか。
705: // オープン出来れば、fcb に諸々をセットして 0 を返す。
706: // o fcb->date, fcb->time に MS-DOS 形式の日付時刻
707: // o fcb->size にファイルサイズ
708: // o fcb->fd にディスクリプタ
709: // 失敗すればステータスコードを返す。
710: uint32
711: HostWindrv::Open(Windrv::FCB *fcb, const std::string& path)
712: {
713: assert(fcb);
714:
715: // 全世界的に同じ値だと思うけど…。
716: int openmode;
717: switch (fcb->GetMode()) {
718: case Human68k::OPEN_RDONLY: openmode = O_RDONLY; break;
719: case Human68k::OPEN_WRONLY: openmode = O_WRONLY; break;
720: case Human68k::OPEN_RDWR: openmode = O_RDWR; break;
721: default:
722: return Human68k::RES_INVALID_MODE;
723: }
724:
725: std::string fullname = rootpath + path;
726: fcb->fd = open(fullname.c_str(), openmode);
727: if (fcb->fd < 0) {
728: putmsg(1, "%s: open %s: %s", __func__,
729: fullname.c_str(), strerror(errno));
730: return Human68k::RES_INVALID_FUNC; // ?
731: }
732:
733: struct stat st;
734: int r = fstat(fcb->fd, &st);
735: if (r < 0) {
736: putmsg(1, "%s: fstat %s: %s", __func__,
737: fullname.c_str(), strerror(errno));
738: Close(fcb);
739: return Human68k::RES_INVALID_FUNC; // ?
740: }
741:
742: fcb->SetDateTime(Human68k::Unixtime2DateTime(st.st_mtime));
743: fcb->SetSize(st.st_size);
744: return 0;
745: }
746:
747: // FCB をクローズする。
748: void
749: HostWindrv::Close(Windrv::FCB *fcb)
750: {
751: if (fcb->fd >= 0) {
752: close(fcb->fd);
753: fcb->fd = -1;
754: }
755: }
756:
757: // fcb(->fd) から dst に len バイト読み込む。
758: // ここでは fcb は更新不要。
759: uint32
760: HostWindrv::Read(Windrv::FCB *fcb, void *dst, uint32 len)
761: {
762: assert(fcb);
763:
764: auto n = read(fcb->fd, dst, len);
765: if (n < 0) {
766: putmsg(1, "%s: %s", __func__, strerror(errno));
767: return -1;
768: }
769: return (uint32)n;
770: }
771:
772: // fcb(->fd) に src から len バイトを書き出す。
773: // ここでは fcb は更新不要。
774: uint32
775: HostWindrv::Write(Windrv::FCB *fcb, const void *src, uint32 len)
776: {
777: assert(fcb);
778:
779: auto n = write(fcb->fd, src, len);
780: if (n < 0) {
781: putmsg(1, "%s: %s", __func__, strerror(errno));
782: return -1;
783: }
784: return (uint32)n;
785: }
786:
787: // シークする。
788: // 戻り値はシーク後の先頭からの現在位置。
789: // ここでは fcb は更新不要。
790: uint32
791: HostWindrv::Seek(Windrv::FCB *fcb, int32 offset, int whence)
792: {
793: assert(fcb);
794:
795: auto n = lseek(fcb->fd, (off_t)offset, whence);
796: if (n < 0) {
797: putmsg(1, "%s: lseek($%x, %d): %s", __func__,
798: offset, whence, strerror(errno));
799: return -1;
800: }
801:
802: return (uint32)n;
803: }
804:
805: // フラッシュ。
806: uint32
807: HostWindrv::Flush()
808: {
809: return 0;
810: }
811:
812: // ファイルの更新日時(MS-DOS形式)を取得する。
813: uint32
814: HostWindrv::GetFileTime(Windrv::FCB *fcb)
815: {
816: assert(fcb);
817:
818: struct stat st;
819: int r = fstat(fcb->fd, &st);
820: if (r < 0) {
821: putmsg(1, "%s: fstat: %s", __func__, strerror(errno));
822: return -1;
823: }
824:
825: uint32 result = Human68k::Unixtime2DateTime(st.st_mtime);
826: return result;
827: }
828:
829: // ファイルの更新日時を設定する。
830: // datetime は MS-DOS 形式。
831: uint32
832: HostWindrv::SetFileTime(Windrv::FCB *fcb, uint32 datetime)
833: {
834: assert(fcb);
835:
836: // [0] が atime、[1] が mtime。
837: struct timeval times[2];
1.1.1.5 ! root 838: uint64 now = get_hosttime_usec();
! 839: times[0].tv_sec = now / 1000'000U;
! 840: times[0].tv_usec = now % 1000'000U;
! 841: times[1].tv_sec = Human68k::DateTime2Unixtime(datetime);
1.1 root 842: times[1].tv_usec = 0;
843: int r = futimes(fcb->fd, times);
844: if (r < 0) {
845: putmsg(1, "%s: futimes: %s", __func__, strerror(errno));
846: return -1;
847: }
848: return 0;
849: }
850:
851: #if defined(HAVE_STAT_ST_TIMESPEC)
852: /*static*/ uint64
853: HostWindrv::timespec2nsec(const struct timespec& ts)
854: {
1.1.1.5 ! root 855: return sec_to_nsec(ts.tv_sec) + ts.tv_nsec;
1.1 root 856: }
857: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.