Annotation of nono/host/hostwindrv.cpp, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.