Annotation of nono/host/hostwindrv.cpp, revision 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.