|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: // UFS
8:
9: #include "ufs.h"
10: #include "ffs_dir.h"
11: #include "scsidev.h"
12:
13: //
14: // ファイルシステムハンドルっぽいもの
15: //
16:
17: // コンストラクタ
18: Filesys::Filesys(Device *parent_)
19: {
20: parent = parent_;
21: fs = &fs0;
22: }
23:
24: // デストラクタ
25: Filesys::~Filesys()
26: {
27: }
28:
29: // パーティションを FFS としてオープンする(マウントのようなイメージ)。
30: // start, size はこのパーティションの開始セクタとセクタ数。
31: // 成功すれば true を返す。
32: // 失敗すれば errstr にエラーメッセージをセットして false を返す。
33: bool
34: Filesys::Mount(SCSIHD *hd_, uint32 start, uint32 size)
35: {
36: hd = hd_;
37: part_start = start;
38: part_size = size;
39:
40: // FFS の先頭にはブートブロックがあり、続けてスーパーブロックがある。
41: // ブートブロックはブートローダのための領域で、ここもファイルシステムの
42: // 内側という認識のようだ。
43: //
44: // ブートブロックの大きさはおそらくファイルシステムごとに固定で、
45: // FFSv1(?) なら 8KB のようだ。ここは LUNA-I の ROM が解釈できるのと
46: // 同じファイルシステムだけ扱えればよいので、8KB 固定とする。
47: // 続いてスーパーブロックがある。ここにこのファイルシステムのパラメータ
48: // が書いてある。
49: //
50: // +-------------+ 0 <- part_start (パーティション先頭)
51: // | BootBlock |
52: // +-------------+ 8KB
53: // | SuperBlock |
54: // | fs_iblkno ---+
55: // +-------------+ |
56: // : : |
57: // |
58: // : : |
59: // +-------------+ <-+
60: // | InodeBlock? |
61: //
62: // スーパーブロックの構造は <ufs/ffs/fs.h> の struct fs 参照。
63:
64: // スーパーブロックを読む (ここはまだ PeekBlock() は使えない)
65: hd->PeekImage(&fs0, part_start * 512 + 8192, sizeof(fs0));
66: // 読み込んだスーパーブロックはゲストエンディアン(BE) なので
67: // 使う前に (もう面倒なのでほぼ全部) ホストエンディアンにしておく。
68: // ヘッダに定義してあるマクロをそのまま使いたいからだけど、正気かなこれ。
69: fs->fs_firstfield = be32toh(fs->fs_firstfield);
70: fs->fs_sblkno = be32toh(fs->fs_sblkno);
71: fs->fs_cblkno = be32toh(fs->fs_cblkno);
72: fs->fs_iblkno = be32toh(fs->fs_iblkno);
73: fs->fs_dblkno = be32toh(fs->fs_dblkno);
74: fs->fs_old_cgoffset = be32toh(fs->fs_old_cgoffset);
75: fs->fs_old_cgmask = be32toh(fs->fs_old_cgmask);
76: fs->fs_old_time = be32toh(fs->fs_old_time);
77: fs->fs_old_size = be32toh(fs->fs_old_size);
78: fs->fs_old_dsize = be32toh(fs->fs_old_dsize);
79: fs->fs_ncg = be32toh(fs->fs_ncg);
80: fs->fs_bsize = be32toh(fs->fs_bsize);
81: fs->fs_fsize = be32toh(fs->fs_fsize);
82: fs->fs_frag = be32toh(fs->fs_frag);
83: fs->fs_minfree = be32toh(fs->fs_minfree);
84: fs->fs_old_rotdelay = be32toh(fs->fs_old_rotdelay);
85: fs->fs_old_rps = be32toh(fs->fs_old_rps);
86: fs->fs_bmask = be32toh(fs->fs_bmask);
87: fs->fs_fmask = be32toh(fs->fs_fmask);
88: fs->fs_bshift = be32toh(fs->fs_bshift);
89: fs->fs_fshift = be32toh(fs->fs_fshift);
90: fs->fs_maxcontig = be32toh(fs->fs_maxcontig);
91: fs->fs_maxbpg = be32toh(fs->fs_maxbpg);
92: fs->fs_fragshift = be32toh(fs->fs_fragshift);
93: fs->fs_fsbtodb = be32toh(fs->fs_fsbtodb);
94: fs->fs_sbsize = be32toh(fs->fs_sbsize);
95: //fs->fs_spare1[2]
96: fs->fs_nindir = be32toh(fs->fs_nindir);
97: fs->fs_inopb = be32toh(fs->fs_inopb);
98: fs->fs_old_nspf = be32toh(fs->fs_old_nspf);
99: fs->fs_optim = be32toh(fs->fs_optim);
100: fs->fs_old_npsect = be32toh(fs->fs_old_npsect);
101: fs->fs_old_interleave = be32toh(fs->fs_old_interleave);
102: fs->fs_old_trackskew = be32toh(fs->fs_old_trackskew);
103: fs->fs_id[0] = be32toh(fs->fs_id[0]);
104: fs->fs_id[1] = be32toh(fs->fs_id[1]);
105: fs->fs_old_csaddr = be32toh(fs->fs_old_csaddr);
106: fs->fs_cssize = be32toh(fs->fs_cssize);
107: fs->fs_cgsize = be32toh(fs->fs_cgsize);
108: fs->fs_spare2 = be32toh(fs->fs_spare2);
109: fs->fs_old_nsect = be32toh(fs->fs_old_nsect);
110: fs->fs_old_spc = be32toh(fs->fs_old_spc);
111: fs->fs_old_ncyl = be32toh(fs->fs_old_ncyl);
112: fs->fs_old_cpg = be32toh(fs->fs_old_cpg);
113: fs->fs_ipg = be32toh(fs->fs_ipg);
114: fs->fs_fpg = be32toh(fs->fs_fpg);
115: //struct csum fs_old_cstotal
116: //u_char fs_fsmnt[MAXMNTLEN]
117: //u_char fs_volname[MAXVOLLEN]
118: fs->fs_swuid = be64toh(fs->fs_swuid);
119: fs->fs_pad = be32toh(fs->fs_pad);
120: fs->fs_cgrotor = be32toh(fs->fs_cgrotor);
121: //void *fs_ocsp[NOCSPTRS]
122: //struct csum *fs_csp
123: //uint32_t *fs_maxcluster
124: //u_char *fs_active
125: fs->fs_old_cpc = be32toh(fs->fs_old_cpc);
126: fs->fs_maxbsize = be32toh(fs->fs_maxbsize);
127: fs->fs_journal_flags = be32toh(fs->fs_journal_flags);
128: //uint64_t fs->fs_journallocs[4]
129: fs->fs_quota_magic = be32toh(fs->fs_quota_magic);
130: //uint64_t fs->fs_quotafile[2]
131: //uint64_t fs->fs_sparecon64[9]
132: fs->fs_sblockloc = be64toh(fs->fs_sblockloc);
133: //struct csum_total fs_cstotal
134: fs->fs_time = be64toh(fs->fs_time);
135: fs->fs_size = be64toh(fs->fs_size);
136: fs->fs_dsize = be64toh(fs->fs_dsize);
137: fs->fs_csaddr = be64toh(fs->fs_csaddr);
138: fs->fs_pendingblocks = be64toh(fs->fs_pendingblocks);
139: fs->fs_pendinginodes = be32toh(fs->fs_pendinginodes);
140: // uint32_t fs->fs_snapinum[FSMAXSNAP]
141: fs->fs_avgfilesize = be32toh(fs->fs_avgfilesize);
142: fs->fs_avgfpdir = be32toh(fs->fs_avgfpdir);
143: fs->fs_save_cgsize = be32toh(fs->fs_save_cgsize);
144: // uint32_t fs->fs_sparecon32[26]
145: fs->fs_flags = be32toh(fs->fs_flags);
146: fs->fs_contigsumsize = be32toh(fs->fs_contigsumsize);
147: fs->fs_maxsymlinklen = be32toh(fs->fs_maxsymlinklen);
148: fs->fs_old_inodefmt = be32toh(fs->fs_old_inodefmt);
149: fs->fs_maxfilesize = be64toh(fs->fs_maxfilesize);
150: fs->fs_qbmask = be64toh(fs->fs_qbmask);
151: fs->fs_qfmask = be64toh(fs->fs_qfmask);
152: fs->fs_state = be32toh(fs->fs_state);
153: fs->fs_old_postblformat = be32toh(fs->fs_old_postblformat);
154: fs->fs_old_nrpos = be32toh(fs->fs_old_nrpos);
155: // uint32_t fs->fs_spare5[2]
156: fs->fs_magic = be32toh(fs->fs_magic);
157:
158: // マジック
159: if (fs->fs_magic != FS_UFS1_MAGIC) {
160: errstr = string_format("Bad superblock magic 0x%08x (!= 0x%08x)",
161: fs->fs_magic, FS_UFS1_MAGIC);
162: return false;
163: }
164:
165: // なんとなく主要なパラメータを表示してみるか
166: if (parent->loglevel >= 2) {
167: uint32 sblkno = fs->fs_sblkno; // super block in filesys
168: uint32 cblkno = fs->fs_cblkno; // cylinder group block in filesys
169: uint32 iblkno = fs->fs_iblkno; // inode block in filesys
170: uint32 dblkno = fs->fs_dblkno; // data block in filesys
171: uint32 bsize = fs->fs_bsize; // size of basic blocks
172: uint32 ipg = fs->fs_ipg; // inodes per group
173: uint32 fpg = fs->fs_fpg; // blocks per group x fs_frag
174: uint32 old_cgoffset = fs->fs_old_cgoffset; // cg. offset in cyl.
175: uint32 fsbtodb= fs->fs_fsbtodb; // shift constant
176:
177: parent->putmsgn("%s fs.fs_sblkno=0x%x (0x%x)",
178: __func__, sblkno, sblkno * 512);
179: parent->putmsgn("%s fs.fs_cblkno=0x%x (0x%x)",
180: __func__, cblkno, cblkno * 512);
181: parent->putmsgn("%s fs.fs_dblkno=0x%x (0x%x)",
182: __func__, dblkno, dblkno * 512);
183: parent->putmsgn("%s fs.fs_iblkno=0x%x (0x%x)",
184: __func__, iblkno, iblkno * 512);
185: parent->putmsgn("%s fs.fs_bsize=0x%x", __func__, bsize);
186: parent->putmsgn("%s fs.fs_ipg=0x%x", __func__, ipg);
187: parent->putmsgn("%s fs.fs_fpg=0x%x", __func__, fpg);
188: parent->putmsgn("%s fs.fs_old_cgoffset=0x%x", __func__, old_cgoffset);
189: parent->putmsgn("%s fs.fs_fsbtodb=%d", __func__, fsbtodb);
190: }
191:
192: return true;
193: }
194:
195: // ファイル名からファイルをオープンする。
196: // オープンできれば file を埋めて true を返す。
197: // オープンできなければ errstr にエラーメッセージをセットして false を返す。
198: bool
199: Filesys::OpenFile(inodefile& file, const std::string& filename_)
200: {
201: std::string filename(filename_);
202: std::vector<std::string> path;
203: int idx = 0;
204: inodefile dir(parent);
205:
206: enum {
207: STATE_REG, // 通常ファイル (終了条件)
208: STATE_PATH, // パス解析
209: STATE_DIR, // ディレクトリ読み込み
210: STATE_FIND, // ディレクトリからファイルを検索
211: STATE_LINK, // シンボリックリンク
212: } state;
213:
214: // DKFILE の先頭に '/' がなければここで足しておく。
215: // ファイル名を分解する際にルートディレクトリマークが必要なので。
216: if (filename[0] != '/') {
217: filename.insert(0, 1, '/');
218: }
219: state = STATE_PATH;
220:
221: while (state != STATE_REG) {
222: switch (state) {
223: case STATE_PATH:
224: // ファイル名をパスに分解
225: // filename にパス名をセットしてからここに来ること。
226: parent->putmsgf(1, lstr("%s Path filename=|%s|",
227: lam_func, filename.c_str()));
228: if (filename.empty()) {
229: errstr = "Filename empty?";
230: return false;
231: }
232: path.clear();
233: for (int s = 0; s < filename.size(); ) {
234: int p = filename.find('/', s);
235: if (p == std::string::npos) {
236: p = filename.size();
237: }
238: // '/' が連続していなければパスコンポーネント
239: if (p != s) {
240: path.push_back(filename.substr(s, p - s));
241: }
242: s = p + 1;
243: }
244: idx = 0;
245: if (parent->loglevel >= 1) {
246: for (int i = 0; i < path.size(); i++) {
247: parent->putmsgn("%s path[%d]=|%s|",
248: __func__, i, path[i].c_str());
249: }
250: }
251: // パスが '/' から始まっていればルートディレクトリの読み込みから。
252: // そうでなければ現在のディレクトリでファイルの検索から。
253: if (filename[0] == '/') {
254: // ルートディレクトリの inode 番号は伝統的に #2 と
255: // いうことになっている。
256: dir.ino = 2;
257: state = STATE_DIR;
258: } else {
259: state = STATE_FIND;
260: }
261: break;
262:
263: case STATE_DIR:
264: // inode 番号からディレクトリエントリを取得。
265: // dir.ino をセットしてからここに来ること。
266: parent->putmsgf(1, lstr("%s Readdir ino=%d", lam_func, dir.ino));
267: Readi(dir);
268: // 中身(ディレクトリエントリ) を取得
269: ReadData(dir);
270: state = STATE_FIND;
271: break;
272:
273: case STATE_FIND:
274: // ディレクトリエントリ dir.data から
275: // ファイル名 path[idx] を探す。
276: parent->putmsgf(1, lstr("%s FindFile %s",
277: lam_func, path[idx].c_str()));
278: file.ino = dir.FindFile(path[idx]);
279: if (file.ino == 0) {
280: errstr = string_format("file not found");
281: return false;
282: }
283: // ファイルの inode 情報を取得。
284: Readi(file);
285: switch (be16toh(file.di.di_mode) & IFMT) {
286: case IFREG: // 通常ファイル
287: state = STATE_REG;
288: break;
289:
290: case IFLNK: // シンボリックリンク
291: state = STATE_LINK;
292: break;
293:
294: case IFDIR: // ディレクトリ
295: dir.ino = file.ino;
296: idx++;
297: // パスの最後のコンポーネントがまだディレクトリならエラー
298: if (idx >= path.size()) {
299: errstr = "is a directory";
300: return false;
301: }
302: state = STATE_DIR;
303: break;
304:
305: default: // それ以外は対応してないし来ないはず
306: errstr = string_format("invalid filemode %06o",
307: be16toh(file.di.di_mode));
308: return false;
309: }
310: break;
311:
312: case STATE_LINK:
313: // リンク先ファイル名を取り出す
314: parent->putmsgf(1, lstr("%s Readlink", lam_func));
315: ReadData(file);
316: filename = std::string((const char *)file.data.data(),
317: file.data.size());
318: // パス解析へ
319: state = STATE_PATH;
320: break;
321:
322: default:
323: __unreachable();
324: }
325: }
326: // 通常ファイルに到達
327:
328: if (idx != path.size() - 1) {
329: // パスの最後のコンポーネントじゃなければエラー
330: errstr = "path not found";
331: return false;
332: }
333:
334: return true;
335: }
336:
337: // inode(.di) で指定されたファイル(またはディレクトリ)の本文を
338: // inode.data に返す。
339: // 今の所エラーは起きない。
340: void
341: Filesys::ReadData(inodefile& inode)
342: {
343: // di (ufs1_dinode) にはこの inode (ファイルまたはディレクトリ)の
344: // データの格納ブロックが記録されている。ディレクトリとは中身が
345: // ディレクトリエントリであるファイルと同じ構造のもの。
346: // di_db[] (直接ブロック)
347: // di_ib[] (間接ブロック)
348: // di_blocks (実際に使用しているブロック数)
349: //
350: // 直接ブロック di_db[] は12要素の配列で、データブロックの位置が
351: // 記録されている (未使用なら 0 のようだ)。
352: // データブロックは fs->fs_bsize でたぶん基本 4KB のやつ。
353: // これもパーティション先頭からのセクタ番号で表記される。なので
354: // ディスク上で連続しているファイルなら di_db[N] と di_db[N + 1] は
355: // 8 ずつ増えてるのが見えるはず。
356: // 例えば以下のサンプル断片は、直接ブロック2つでセクタ 100 から
357: // セクタ 115 までの連続する 16KB を所有している様子を示している。
358: //
359: // #100 #101 #102 #107
360: // +------+------+------+- .. -+------+
361: // di_db[0] = 100 ---> | Data | Data | Data | | Data |
362: // +------+------+------+- .. -+------+
363: //
364: // #108 #115
365: // +------+------+------+- .. -+------+
366: // di_db[1] = 108 ---> | Data | Data | Data | | Data |
367: // +------+------+------+- .. -+------+
368: //
369: // 間接ブロック di_ib[0], [1], [2] はそれぞれ一次、二次、三次の
370: // 間接記録になっている。一次間接だとこういう感じ。
371: //
372: // #120
373: // +--------+ +------+- ..
374: // di_ib[0] = 120 ---> | #200 ------> #200 | Data |
375: // | | +------+- ..
376: // | #208 ------> #208 | Data |
377: // +--------+ +------+- ..
378: //
379:
380: uint32 ino = inode.ino;
381: struct ufs1_dinode *di = &inode.di;
382: std::vector<uint8>& data = inode.data;
383: uint64 bsize64 = (uint64)fs->fs_bsize;
384:
385: uint64 di_size = be64toh(di->di_size);
386: uint32 di_blocks = be32toh(di->di_blocks);
387: // di->di_db[] にファイル(またはディレクトリ)の内容の位置が並んでいる
388: if (parent->loglevel >= 2) {
389: parent->putmsgn("%s di[%d].di_blocks=%u", __func__, ino, di_blocks);
390:
391: std::string dbstr;
392: for (int i = 0; i < UFS_NDADDR; i++) {
393: dbstr += string_format(" %u", be32toh(di->di_db[i]));
394: }
395: parent->putmsgn("%s di[%d].di_db[]=%s, di_ib[]=%u %u %u",
396: __func__, ino, dbstr.c_str() + 1,
397: be32toh(di->di_ib[0]),
398: be32toh(di->di_ib[1]),
399: be32toh(di->di_ib[2]));
400: }
401:
402: uint64 offset = 0; // 次に読み込む位置 (ここまでに読み込んだバイト数)
403:
404: // サイズが分かっているのであらかじめ確保しておく
405: data.resize(di_size);
406:
407: // 直接ブロック
408: for (int i = 0; i < countof(di->di_db); i++) {
409: uint64 remain = di_size - offset; // 残りバイト数
410: uint32 nbytes = std::min(remain, bsize64); // 今回読み込み分
411: PeekBlock(&data[offset], be32toh(di->di_db[i]), nbytes);
412: offset += nbytes;
413: if (offset >= di_size) {
414: return;
415: }
416: }
417: // 一次間接ブロック
418: std::vector<uint32> ib0buf(fs->fs_bsize / sizeof(uint32));
419: PeekBlock(&ib0buf[0], be32toh(di->di_ib[0]), fs->fs_bsize);
420: if (parent->loglevel >= 2) {
421: std::string distr;
422: for (const auto& x : ib0buf) {
423: if (x == 0)
424: break;
425: distr += string_format(" %u", be32toh(x));
426: }
427: parent->putmsgn("%s di[%d].di_ib[0]=%s",
428: __func__, ino, distr.c_str() + 1);
429: }
430: for (const auto& x : ib0buf) {
431: uint64 remain = di_size - offset; // 残りバイト数
432: uint32 nbytes = std::min(remain, bsize64); // 今回読み込み分
433: PeekBlock(&data[offset], be32toh(x), nbytes);
434: offset += nbytes;
435: if (offset >= di_size) {
436: return;
437: }
438: }
439:
440: // 二次、三次ブロックは未対応
441: if (offset < di_size) {
442: PANIC("di_ib[1] not supported");
443: }
444: }
445:
446: // inode.ino で指定された disk inode 情報を inode.di に返す。
447: // 今の所エラーは起きない。
448: void
449: Filesys::Readi(inodefile& inode)
450: {
451: // di は inode のディスク上の記録構造で 128 バイト長。
452: // 詳細は <ufs/ufs/dinode.h> の ufs1_dinode 参照。
453: // 基本的には inode ブロックにこの dinode が配列のように並んでいるが
454: // シリンダグループを越えるためには謎のマクロを使う。
455: // sys/lib/libsa/ufs.c 参照。
456: //
457: // dinode のうち、ここで関係するかも知れないのは
458: // di_mode (のうちファイルモード)
459: // di_size (ファイルサイズ)
460: // di_db[] (直接ブロック)
461: // di_ib[] (間接ブロック)
462: // di_blocks (ブロック数)
463: // くらいか。
464:
465: uint32 ino = inode.ino;
466:
467: // ino が含まれるブロックを読み込む。
468: // ino_to_fsba(fs, ino) が指定の ino が含まれるブロックを返す。
469: // ino_to_fsbo(fs, ino) はそのブロック内での ino のオフセットを返す。
470: // 例えば inode=2 の fsba が 0x18、fsbo が 2 なら、0x18ブロック目の
471: // [2] からつまり256バイト目からとなる。
472: std::vector<char> buf(fs->fs_bsize);
473: uint64 fsba = ino_to_fsba(fs, ino);
474: uint32 fsbo = ino_to_fsbo(fs, ino);
475: PeekBlock(&buf[0], fsba, buf.size());
476:
477: // ino で示される inode のディスク上構造を
478: // (ゲストエンディアンのまま) di にコピーして持っておく。
479: struct ufs1_dinode *di = &inode.di;
480: memcpy(di, &((ufs1_dinode *)(&buf[0]))[fsbo], sizeof(*di));
481: uint16 di_mode = be16toh(di->di_mode);
482: uint16 di_nlink = be16toh(di->di_nlink);
483: uint64 di_size = be64toh(di->di_size);
484: parent->putmsgf(2,
485: lstr("%s di[%d].di_mode=%05o di_nlink=%d di_size=%" PRIu64,
486: lam_func, ino, di_mode, di_nlink, di_size));
487: }
488:
489: // ディスクの指定ブロック b の先頭から len バイト読み出して buf に返す。
490: // buf は少なくとも len バイト確保してあること。
491: void
492: Filesys::PeekBlock(void *buf, uint32 b, uint32 len) const
493: {
494: // FFS_FSBTODB(fs, b) がブロック番号 b をセクタ番号に変換する。
495: uint32 sector = part_start + FFS_FSBTODB(fs, b);
496: hd->PeekImage(buf, sector * 512, len);
497: }
498:
499:
500: //
501: // inode 情報クラス
502: //
503:
504: // コンストラクタ
505: inodefile::inodefile(Device *parent_)
506: {
507: parent = parent_;
508: }
509:
510: // デストラクタ
511: inodefile::~inodefile()
512: {
513: }
514:
515: // ディレクトリエントリ dirbuf から name で指定されるファイル名に対応する
516: // inode 番号を返す。見付からなければ 0 を返す。
517: uint32
518: inodefile::FindFile(const std::string& name)
519: {
520: // ディレクトリエントリは struct direct が可変長で並んでいる。
521: // ../host/ffs_dir.h (元は NetBSD の <ufs/ufs/dir.h>) 参照。
522: // d->d_reclen がこの1つ分のエントリの長さなので、次のエントリ位置は
523: // このエントリの頭から d->d_reclen を足したところになる。
524: // 最終エントリの d_reclen を足すとバッファ末尾になるようになっている
525: // ようだ。
526: const char *dp = (const char *)(data.data());
527: const char *dend = dp + data.size();
528: uint32 file_ino;
529: uint16 reclen;
530:
531: for (; dp < dend; dp += reclen) {
532: const struct direct *d = (const struct direct *)dp;
533: file_ino = be32toh(d->d_fileno);
534: reclen = be16toh(d->d_reclen);
535:
536: std::string str = string_format("ino=%u reclen=%u name=|%s|",
537: file_ino, reclen, d->d_name);
538: if (d->d_reclen == 0) {
539: parent->putmsgf(1, lstr("%s reclen=0 !", lam_func));
540: break;
541: }
542:
543: if (strcmp(name.c_str(), d->d_name) == 0) {
544: parent->putmsgf(2, lstr("%s %s found", lam_func, str.c_str()));
545: return file_ino;
546: } else {
547: parent->putmsgf(2, lstr("%s %s skip", lam_func, str.c_str()));
548: }
549: }
550: parent->putmsgf(2, lstr("%s %s not found", lam_func, name.c_str()));
551: return 0;
552: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.