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