SandboXP

Форк
0
/
9p.js 
855 строк · 32.8 Кб
1
// -------------------------------------------------
2
// --------------------- 9P ------------------------
3
// -------------------------------------------------
4
// Implementation of the 9p filesystem device following the
5
// 9P2000.L protocol ( https://code.google.com/p/diod/wiki/protocol )
6

7
"use strict";
8

9
// Feature bit (bit position) for mount tag.
10
const VIRTIO_9P_F_MOUNT_TAG = 0;
11
// Assumed max tag length in bytes.
12
const VIRTIO_9P_MAX_TAGLEN = 254;
13

14
// TODO
15
// flush
16

17
var EPERM = 1;       /* Operation not permitted */
18
var ENOENT = 2;      /* No such file or directory */
19
var EEXIST = 17;      /* File exists */
20
var EINVAL = 22;     /* Invalid argument */
21
var EOPNOTSUPP = 95;  /* Operation is not supported */
22
var ENOTEMPTY = 39;  /* Directory not empty */
23
var EPROTO    = 71;  /* Protocol error */
24

25
var P9_SETATTR_MODE = 0x00000001;
26
var P9_SETATTR_UID = 0x00000002;
27
var P9_SETATTR_GID = 0x00000004;
28
var P9_SETATTR_SIZE = 0x00000008;
29
var P9_SETATTR_ATIME = 0x00000010;
30
var P9_SETATTR_MTIME = 0x00000020;
31
var P9_SETATTR_CTIME = 0x00000040;
32
var P9_SETATTR_ATIME_SET = 0x00000080;
33
var P9_SETATTR_MTIME_SET = 0x00000100;
34

35
var P9_STAT_MODE_DIR = 0x80000000;
36
var P9_STAT_MODE_APPEND = 0x40000000;
37
var P9_STAT_MODE_EXCL = 0x20000000;
38
var P9_STAT_MODE_MOUNT = 0x10000000;
39
var P9_STAT_MODE_AUTH = 0x08000000;
40
var P9_STAT_MODE_TMP = 0x04000000;
41
var P9_STAT_MODE_SYMLINK = 0x02000000;
42
var P9_STAT_MODE_LINK = 0x01000000;
43
var P9_STAT_MODE_DEVICE = 0x00800000;
44
var P9_STAT_MODE_NAMED_PIPE = 0x00200000;
45
var P9_STAT_MODE_SOCKET = 0x00100000;
46
var P9_STAT_MODE_SETUID = 0x00080000;
47
var P9_STAT_MODE_SETGID = 0x00040000;
48
var P9_STAT_MODE_SETVTX = 0x00010000;
49

50
const P9_LOCK_TYPE_RDLCK = 0;
51
const P9_LOCK_TYPE_WRLCK = 1;
52
const P9_LOCK_TYPE_UNLCK = 2;
53
const P9_LOCK_TYPES = Object.freeze(["shared", "exclusive", "unlock"]);
54

55
const P9_LOCK_FLAGS_BLOCK = 1;
56
const P9_LOCK_FLAGS_RECLAIM = 2;
57

58
const P9_LOCK_SUCCESS = 0;
59
const P9_LOCK_BLOCKED = 1;
60
const P9_LOCK_ERROR = 2;
61
const P9_LOCK_GRACE = 3;
62

63
var FID_NONE = -1;
64
var FID_INODE = 1;
65
var FID_XATTR = 2;
66

67
/**
68
 * @constructor
69
 *
70
 * @param {FS} filesystem
71
 * @param {CPU} cpu
72
 */
73
function Virtio9p(filesystem, cpu, bus) {
74
    /** @type {FS} */
75
    this.fs = filesystem;
76

77
    /** @const @type {BusConnector} */
78
    this.bus = bus;
79

80
    //this.configspace = [0x0, 0x4, 0x68, 0x6F, 0x73, 0x74]; // length of string and "host" string
81
    //this.configspace = [0x0, 0x9, 0x2F, 0x64, 0x65, 0x76, 0x2F, 0x72, 0x6F, 0x6F, 0x74 ]; // length of string and "/dev/root" string
82
    this.configspace_tagname = [0x68, 0x6F, 0x73, 0x74, 0x39, 0x70]; // "host9p" string
83
    this.configspace_taglen = this.configspace_tagname.length; // num bytes
84
    this.VERSION = "9P2000.L";
85
    this.BLOCKSIZE = 8192; // Let's define one page.
86
    this.msize = 8192; // maximum message size
87
    this.replybuffer = new Uint8Array(this.msize*2); // Twice the msize to stay on the safe site
88
    this.replybuffersize = 0;
89

90
    this.fids = [];
91

92
    /** @type {VirtIO} */
93
    this.virtio = new VirtIO(cpu,
94
    {
95
        name: "virtio-9p",
96
        pci_id: 0x06 << 3,
97
        device_id: 0x1049,
98
        subsystem_device_id: 9,
99
        common:
100
        {
101
            initial_port: 0xA800,
102
            queues:
103
            [
104
                {
105
                    size_supported: 32,
106
                    notify_offset: 0,
107
                },
108
            ],
109
            features:
110
            [
111
                VIRTIO_9P_F_MOUNT_TAG,
112
                VIRTIO_F_VERSION_1,
113
                VIRTIO_F_RING_EVENT_IDX,
114
                VIRTIO_F_RING_INDIRECT_DESC,
115
            ],
116
            on_driver_ok: () => {},
117
        },
118
        notification:
119
        {
120
            initial_port: 0xA900,
121
            single_handler: false,
122
            handlers:
123
            [
124
                (queue_id) =>
125
                {
126
                    if(queue_id !== 0)
127
                    {
128
                        dbg_assert(false, "Virtio9P Notified for non-existent queue: " + queue_id +
129
                            " (expected queue_id of 0)");
130
                        return;
131
                    }
132
                    while(this.virtqueue.has_request())
133
                    {
134
                        const bufchain = this.virtqueue.pop_request();
135
                        this.ReceiveRequest(bufchain);
136
                    }
137
                    this.virtqueue.notify_me_after(0);
138
                    // Don't flush replies here: async replies are not completed yet.
139
                },
140
            ],
141
        },
142
        isr_status:
143
        {
144
            initial_port: 0xA700,
145
        },
146
        device_specific:
147
        {
148
            initial_port: 0xA600,
149
            struct:
150
            [
151
                {
152
                    bytes: 2,
153
                    name: "mount tag length",
154
                    read: () => this.configspace_taglen,
155
                    write: data => { /* read only */ },
156
                },
157
            ].concat(v86util.range(VIRTIO_9P_MAX_TAGLEN).map(index =>
158
                ({
159
                    bytes: 1,
160
                    name: "mount tag name " + index,
161
                    // Note: configspace_tagname may have changed after set_state
162
                    read: () => this.configspace_tagname[index] || 0,
163
                    write: data => { /* read only */ },
164
                })
165
            )),
166
        },
167
    });
168
    this.virtqueue = this.virtio.queues[0];
169
}
170

171
Virtio9p.prototype.get_state = function()
172
{
173
    var state = [];
174

175
    state[0] = this.configspace_tagname;
176
    state[1] = this.configspace_taglen;
177
    state[2] = this.virtio;
178
    state[3] = this.VERSION;
179
    state[4] = this.BLOCKSIZE;
180
    state[5] = this.msize;
181
    state[6] = this.replybuffer;
182
    state[7] = this.replybuffersize;
183
    state[8] = this.fids.map(function(f) { return [f.inodeid, f.type, f.uid, f.dbg_name]; });
184
    state[9] = this.fs;
185

186
    return state;
187
};
188

189
Virtio9p.prototype.set_state = function(state)
190
{
191
    this.configspace_tagname = state[0];
192
    this.configspace_taglen = state[1];
193
    this.virtio.set_state(state[2]);
194
    this.virtqueue = this.virtio.queues[0];
195
    this.VERSION = state[3];
196
    this.BLOCKSIZE = state[4];
197
    this.msize = state[5];
198
    this.replybuffer = state[6];
199
    this.replybuffersize = state[7];
200
    this.fids = state[8].map(function(f)
201
    {
202
        return { inodeid: f[0], type: f[1], uid: f[2], dbg_name: f[3] };
203
    });
204
    this.fs.set_state(state[9]);
205
};
206

207
// Note: dbg_name is only used for debugging messages and may not be the same as the filename,
208
// since it is not synchronised with renames done outside of 9p. Hard-links, linking and unlinking
209
// operations also mean that having a single filename no longer makes sense.
210
// Set TRACK_FILENAMES = true (in config.js) to sync dbg_name during 9p renames.
211
Virtio9p.prototype.Createfid = function(inodeid, type, uid, dbg_name) {
212
    return {inodeid, type, uid, dbg_name};
213
};
214

215
Virtio9p.prototype.update_dbg_name = function(idx, newname)
216
{
217
    for(const fid of this.fids)
218
    {
219
        if(fid.inodeid === idx) fid.dbg_name = newname;
220
    }
221
};
222

223
Virtio9p.prototype.Reset = function() {
224
    this.fids = [];
225
};
226

227

228
Virtio9p.prototype.BuildReply = function(id, tag, payloadsize) {
229
    dbg_assert(payloadsize >= 0, "9P: Negative payload size");
230
    marshall.Marshall(["w", "b", "h"], [payloadsize+7, id+1, tag], this.replybuffer, 0);
231
    if ((payloadsize+7) >= this.replybuffer.length) {
232
        message.Debug("Error in 9p: payloadsize exceeds maximum length");
233
    }
234
    //for(var i=0; i<payload.length; i++)
235
    //    this.replybuffer[7+i] = payload[i];
236
    this.replybuffersize = payloadsize+7;
237
    return;
238
};
239

240
Virtio9p.prototype.SendError = function (tag, errormsg, errorcode) {
241
    //var size = marshall.Marshall(["s", "w"], [errormsg, errorcode], this.replybuffer, 7);
242
    var size = marshall.Marshall(["w"], [errorcode], this.replybuffer, 7);
243
    this.BuildReply(6, tag, size);
244
};
245

246
Virtio9p.prototype.SendReply = function (bufchain) {
247
    dbg_assert(this.replybuffersize >= 0, "9P: Negative replybuffersize");
248
    bufchain.set_next_blob(this.replybuffer.subarray(0, this.replybuffersize));
249
    this.virtqueue.push_reply(bufchain);
250
    this.virtqueue.flush_replies();
251
};
252

253
Virtio9p.prototype.ReceiveRequest = async function (bufchain) {
254
    // TODO: split into header + data blobs to avoid unnecessary copying.
255
    const buffer = new Uint8Array(bufchain.length_readable);
256
    bufchain.get_next_blob(buffer);
257

258
    const state = { offset : 0 };
259
    var header = marshall.Unmarshall(["w", "b", "h"], buffer, state);
260
    var size = header[0];
261
    var id = header[1];
262
    var tag = header[2];
263
    //message.Debug("size:" + size + " id:" + id + " tag:" + tag);
264

265
    switch(id)
266
    {
267
        case 8: // statfs
268
            size = this.fs.GetTotalSize(); // size used by all files
269
            var space = this.fs.GetSpace();
270
            var req = [];
271
            req[0] = 0x01021997;
272
            req[1] = this.BLOCKSIZE; // optimal transfer block size
273
            req[2] = Math.floor(space/req[1]); // free blocks
274
            req[3] = req[2] - Math.floor(size/req[1]); // free blocks in fs
275
            req[4] = req[2] - Math.floor(size/req[1]); // free blocks avail to non-superuser
276
            req[5] = this.fs.CountUsedInodes(); // total number of inodes
277
            req[6] = this.fs.CountFreeInodes();
278
            req[7] = 0; // file system id?
279
            req[8] = 256; // maximum length of filenames
280

281
            size = marshall.Marshall(["w", "w", "d", "d", "d", "d", "d", "d", "w"], req, this.replybuffer, 7);
282
            this.BuildReply(id, tag, size);
283
            this.SendReply(bufchain);
284
            break;
285

286
        case 112: // topen
287
        case 12: // tlopen
288
            var req = marshall.Unmarshall(["w", "w"], buffer, state);
289
            var fid = req[0];
290
            var mode = req[1];
291
            message.Debug("[open] fid=" + fid + ", mode=" + mode);
292
            var idx = this.fids[fid].inodeid;
293
            var inode = this.fs.GetInode(idx);
294
            message.Debug("file open " + this.fids[fid].dbg_name);
295
            //if (inode.status == STATUS_LOADING) return;
296
            var ret = this.fs.OpenInode(idx, mode);
297

298
            this.fs.AddEvent(this.fids[fid].inodeid,
299
                function() {
300
                    message.Debug("file opened " + this.fids[fid].dbg_name + " tag:"+tag);
301
                    var req = [];
302
                    req[0] = inode.qid;
303
                    req[1] = this.msize - 24;
304
                    marshall.Marshall(["Q", "w"], req, this.replybuffer, 7);
305
                    this.BuildReply(id, tag, 13+4);
306
                    this.SendReply(bufchain);
307
                }.bind(this)
308
            );
309
            break;
310

311
        case 70: // link
312
            var req = marshall.Unmarshall(["w", "w", "s"], buffer, state);
313
            var dfid = req[0];
314
            var fid = req[1];
315
            var name = req[2];
316
            message.Debug("[link] dfid=" + dfid + ", name=" + name);
317

318
            var ret = this.fs.Link(this.fids[dfid].inodeid, this.fids[fid].inodeid, name);
319

320
            if(ret < 0)
321
            {
322
                let error_message = "";
323
                if(ret === -EPERM) error_message = "Operation not permitted";
324
                else
325
                {
326
                    error_message = "Unknown error: " + (-ret);
327
                    dbg_assert(false, "[link]: Unexpected error code: " + (-ret));
328
                }
329
                this.SendError(tag, error_message, -ret);
330
                this.SendReply(bufchain);
331
                break;
332
            }
333

334
            this.BuildReply(id, tag, 0);
335
            this.SendReply(bufchain);
336
            break;
337

338
        case 16: // symlink
339
            var req = marshall.Unmarshall(["w", "s", "s", "w"], buffer, state);
340
            var fid = req[0];
341
            var name = req[1];
342
            var symgt = req[2];
343
            var gid = req[3];
344
            message.Debug("[symlink] fid=" + fid + ", name=" + name + ", symgt=" + symgt + ", gid=" + gid);
345
            var idx = this.fs.CreateSymlink(name, this.fids[fid].inodeid, symgt);
346
            var inode = this.fs.GetInode(idx);
347
            inode.uid = this.fids[fid].uid;
348
            inode.gid = gid;
349
            marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
350
            this.BuildReply(id, tag, 13);
351
            this.SendReply(bufchain);
352
            break;
353

354
        case 18: // mknod
355
            var req = marshall.Unmarshall(["w", "s", "w", "w", "w", "w"], buffer, state);
356
            var fid = req[0];
357
            var name = req[1];
358
            var mode = req[2];
359
            var major = req[3];
360
            var minor = req[4];
361
            var gid = req[5];
362
            message.Debug("[mknod] fid=" + fid + ", name=" + name + ", major=" + major + ", minor=" + minor+ "");
363
            var idx = this.fs.CreateNode(name, this.fids[fid].inodeid, major, minor);
364
            var inode = this.fs.GetInode(idx);
365
            inode.mode = mode;
366
            inode.uid = this.fids[fid].uid;
367
            inode.gid = gid;
368
            marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
369
            this.BuildReply(id, tag, 13);
370
            this.SendReply(bufchain);
371
            break;
372

373

374
        case 22: // TREADLINK
375
            var req = marshall.Unmarshall(["w"], buffer, state);
376
            var fid = req[0];
377
            var inode = this.fs.GetInode(this.fids[fid].inodeid);
378
            message.Debug("[readlink] fid=" + fid + " name=" + this.fids[fid].dbg_name + " target=" + inode.symlink);
379
            size = marshall.Marshall(["s"], [inode.symlink], this.replybuffer, 7);
380
            this.BuildReply(id, tag, size);
381
            this.SendReply(bufchain);
382
            break;
383

384

385
        case 72: // tmkdir
386
            var req = marshall.Unmarshall(["w", "s", "w", "w"], buffer, state);
387
            var fid = req[0];
388
            var name = req[1];
389
            var mode = req[2];
390
            var gid = req[3];
391
            message.Debug("[mkdir] fid=" + fid + ", name=" + name + ", mode=" + mode + ", gid=" + gid);
392
            var idx = this.fs.CreateDirectory(name, this.fids[fid].inodeid);
393
            var inode = this.fs.GetInode(idx);
394
            inode.mode = mode | S_IFDIR;
395
            inode.uid = this.fids[fid].uid;
396
            inode.gid = gid;
397
            marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
398
            this.BuildReply(id, tag, 13);
399
            this.SendReply(bufchain);
400
            break;
401

402
        case 14: // tlcreate
403
            var req = marshall.Unmarshall(["w", "s", "w", "w", "w"], buffer, state);
404
            var fid = req[0];
405
            var name = req[1];
406
            var flags = req[2];
407
            var mode = req[3];
408
            var gid = req[4];
409
            this.bus.send("9p-create", [name, this.fids[fid].inodeid]);
410
            message.Debug("[create] fid=" + fid + ", name=" + name + ", flags=" + flags + ", mode=" + mode + ", gid=" + gid);
411
            var idx = this.fs.CreateFile(name, this.fids[fid].inodeid);
412
            this.fids[fid].inodeid = idx;
413
            this.fids[fid].type = FID_INODE;
414
            this.fids[fid].dbg_name = name;
415
            var inode = this.fs.GetInode(idx);
416
            inode.uid = this.fids[fid].uid;
417
            inode.gid = gid;
418
            inode.mode = mode;
419
            marshall.Marshall(["Q", "w"], [inode.qid, this.msize - 24], this.replybuffer, 7);
420
            this.BuildReply(id, tag, 13+4);
421
            this.SendReply(bufchain);
422
            break;
423

424
        case 52: // lock
425
            var req = marshall.Unmarshall(["w", "b", "w", "d", "d", "w", "s"], buffer, state);
426
            var fid = req[0];
427
            var flags = req[2];
428
            var lock_length = req[4] === 0 ? Infinity : req[4];
429
            var lock_request = this.fs.DescribeLock(req[1], req[3], lock_length, req[5], req[6]);
430
            message.Debug("[lock] fid=" + fid +
431
                ", type=" + P9_LOCK_TYPES[lock_request.type] + ", start=" + lock_request.start +
432
                ", length=" + lock_request.length + ", proc_id=" + lock_request.proc_id);
433

434
            var ret = this.fs.Lock(this.fids[fid].inodeid, lock_request, flags);
435

436
            marshall.Marshall(["b"], [ret], this.replybuffer, 7);
437
            this.BuildReply(id, tag, 1);
438
            this.SendReply(bufchain);
439
            break;
440

441
        case 54: // getlock
442
            var req = marshall.Unmarshall(["w", "b", "d", "d", "w", "s"], buffer, state);
443
            var fid = req[0];
444
            var lock_length = req[3] === 0 ? Infinity : req[3];
445
            var lock_request = this.fs.DescribeLock(req[1], req[2], lock_length, req[4], req[5]);
446
            message.Debug("[getlock] fid=" + fid +
447
                ", type=" + P9_LOCK_TYPES[lock_request.type] + ", start=" + lock_request.start +
448
                ", length=" + lock_request.length + ", proc_id=" + lock_request.proc_id);
449

450
            var ret = this.fs.GetLock(this.fids[fid].inodeid, lock_request);
451

452
            if(!ret)
453
            {
454
                ret = lock_request;
455
                ret.type = P9_LOCK_TYPE_UNLCK;
456
            }
457

458
            var ret_length = ret.length === Infinity ? 0 : ret.length;
459

460
            size = marshall.Marshall(["b", "d", "d", "w", "s"],
461
                [ret.type, ret.start, ret_length, ret.proc_id, ret.client_id],
462
                this.replybuffer, 7);
463

464
            this.BuildReply(id, tag, size);
465
            this.SendReply(bufchain);
466
            break;
467

468
        case 24: // getattr
469
            var req = marshall.Unmarshall(["w", "d"], buffer, state);
470
            var fid = req[0];
471
            var inode = this.fs.GetInode(this.fids[fid].inodeid);
472
            message.Debug("[getattr]: fid=" + fid + " name=" + this.fids[fid].dbg_name + " request mask=" + req[1]);
473
            if(!inode || inode.status === STATUS_UNLINKED)
474
            {
475
                message.Debug("getattr: unlinked");
476
                this.SendError(tag, "No such file or directory", ENOENT);
477
                this.SendReply(bufchain);
478
                break;
479
            }
480
            req[0] |= 0x1000; // P9_STATS_GEN
481

482
            req[0] = req[1]; // request mask
483
            req[1] = inode.qid;
484

485
            req[2] = inode.mode;
486
            req[3] = inode.uid; // user id
487
            req[4] = inode.gid; // group id
488

489
            req[5] = inode.nlinks; // number of hard links
490
            req[6] = (inode.major<<8) | (inode.minor); // device id low
491
            req[7] = inode.size; // size low
492
            req[8] = this.BLOCKSIZE;
493
            req[9] = Math.floor(inode.size/512+1); // blk size low
494
            req[10] = inode.atime; // atime
495
            req[11] = 0x0;
496
            req[12] = inode.mtime; // mtime
497
            req[13] = 0x0;
498
            req[14] = inode.ctime; // ctime
499
            req[15] = 0x0;
500
            req[16] = 0x0; // btime
501
            req[17] = 0x0;
502
            req[18] = 0x0; // st_gen
503
            req[19] = 0x0; // data_version
504
            marshall.Marshall([
505
            "d", "Q",
506
            "w",
507
            "w", "w",
508
            "d", "d",
509
            "d", "d", "d",
510
            "d", "d", // atime
511
            "d", "d", // mtime
512
            "d", "d", // ctime
513
            "d", "d", // btime
514
            "d", "d",
515
            ], req, this.replybuffer, 7);
516
            this.BuildReply(id, tag, 8 + 13 + 4 + 4+ 4 + 8*15);
517
            this.SendReply(bufchain);
518
            break;
519

520
        case 26: // setattr
521
            var req = marshall.Unmarshall(["w", "w",
522
                "w", // mode
523
                "w", "w", // uid, gid
524
                "d", // size
525
                "d", "d", // atime
526
                "d", "d", // mtime
527
            ], buffer, state);
528
            var fid = req[0];
529
            var inode = this.fs.GetInode(this.fids[fid].inodeid);
530
            message.Debug("[setattr]: fid=" + fid + " request mask=" + req[1] + " name=" + this.fids[fid].dbg_name);
531
            if (req[1] & P9_SETATTR_MODE) {
532
                inode.mode = req[2];
533
            }
534
            if (req[1] & P9_SETATTR_UID) {
535
                inode.uid = req[3];
536
            }
537
            if (req[1] & P9_SETATTR_GID) {
538
                inode.gid = req[4];
539
            }
540
            if (req[1] & P9_SETATTR_ATIME) {
541
                inode.atime = Math.floor((new Date()).getTime()/1000);
542
            }
543
            if (req[1] & P9_SETATTR_MTIME) {
544
                inode.mtime = Math.floor((new Date()).getTime()/1000);
545
            }
546
            if (req[1] & P9_SETATTR_CTIME) {
547
                inode.ctime = Math.floor((new Date()).getTime()/1000);
548
            }
549
            if (req[1] & P9_SETATTR_ATIME_SET) {
550
                inode.atime = req[6];
551
            }
552
            if (req[1] & P9_SETATTR_MTIME_SET) {
553
                inode.mtime = req[8];
554
            }
555
            if (req[1] & P9_SETATTR_SIZE) {
556
                await this.fs.ChangeSize(this.fids[fid].inodeid, req[5]);
557
            }
558
            this.BuildReply(id, tag, 0);
559
            this.SendReply(bufchain);
560
            break;
561

562
        case 50: // fsync
563
            var req = marshall.Unmarshall(["w", "d"], buffer, state);
564
            var fid = req[0];
565
            this.BuildReply(id, tag, 0);
566
            this.SendReply(bufchain);
567
            break;
568

569
        case 40: // TREADDIR
570
        case 116: // read
571
            var req = marshall.Unmarshall(["w", "d", "w"], buffer, state);
572
            var fid = req[0];
573
            var offset = req[1];
574
            var count = req[2];
575
            var inode = this.fs.GetInode(this.fids[fid].inodeid);
576
            if (id == 40) message.Debug("[treaddir]: fid=" + fid + " offset=" + offset + " count=" + count);
577
            if (id == 116) message.Debug("[read]: fid=" + fid + " (" + this.fids[fid].dbg_name + ") offset=" + offset + " count=" + count + " fidtype=" + this.fids[fid].type);
578
            if(!inode || inode.status === STATUS_UNLINKED)
579
            {
580
                message.Debug("read/treaddir: unlinked");
581
                this.SendError(tag, "No such file or directory", ENOENT);
582
                this.SendReply(bufchain);
583
                break;
584
            }
585
            if (this.fids[fid].type == FID_XATTR) {
586
                if (inode.caps.length < offset+count) count = inode.caps.length - offset;
587
                for(var i=0; i<count; i++)
588
                    this.replybuffer[7+4+i] = inode.caps[offset+i];
589
                marshall.Marshall(["w"], [count], this.replybuffer, 7);
590
                this.BuildReply(id, tag, 4 + count);
591
                this.SendReply(bufchain);
592
            } else {
593
                this.fs.OpenInode(this.fids[fid].inodeid, undefined);
594
                const inodeid = this.fids[fid].inodeid;
595

596
                count = Math.min(count, this.replybuffer.length - (7 + 4));
597

598
                if (inode.size < offset+count) count = inode.size - offset;
599
                else if(id == 40)
600
                {
601
                    // for directories, return whole number of dir-entries.
602
                    count = this.fs.RoundToDirentry(inodeid, offset + count) - offset;
603
                }
604
                if(offset > inode.size)
605
                {
606
                    // offset can be greater than available - should return count of zero.
607
                    // See http://ericvh.github.io/9p-rfc/rfc9p2000.html#anchor30
608
                    count = 0;
609
                }
610

611
                this.bus.send("9p-read-start", [this.fids[fid].dbg_name]);
612

613
                const data = await this.fs.Read(inodeid, offset, count);
614

615
                this.bus.send("9p-read-end", [this.fids[fid].dbg_name, count]);
616

617
                if(data) {
618
                    this.replybuffer.set(data, 7 + 4);
619
                }
620
                marshall.Marshall(["w"], [count], this.replybuffer, 7);
621
                this.BuildReply(id, tag, 4 + count);
622
                this.SendReply(bufchain);
623
            }
624
            break;
625

626
        case 118: // write
627
            var req = marshall.Unmarshall(["w", "d", "w"], buffer, state);
628
            var fid = req[0];
629
            var offset = req[1];
630
            var count = req[2];
631

632
            const filename = this.fids[fid].dbg_name;
633

634
            message.Debug("[write]: fid=" + fid + " (" + filename + ") offset=" + offset + " count=" + count + " fidtype=" + this.fids[fid].type);
635
            if(this.fids[fid].type === FID_XATTR)
636
            {
637
                // XXX: xattr not supported yet. Ignore write.
638
                this.SendError(tag, "Setxattr not supported", EOPNOTSUPP);
639
                this.SendReply(bufchain);
640
                break;
641
            }
642
            else
643
            {
644
                // XXX: Size of the subarray is unchecked
645
                await this.fs.Write(this.fids[fid].inodeid, offset, count, buffer.subarray(state.offset));
646
            }
647

648
            this.bus.send("9p-write-end", [filename, count]);
649

650
            marshall.Marshall(["w"], [count], this.replybuffer, 7);
651
            this.BuildReply(id, tag, 4);
652
            this.SendReply(bufchain);
653
            break;
654

655
        case 74: // RENAMEAT
656
            var req = marshall.Unmarshall(["w", "s", "w", "s"], buffer, state);
657
            var olddirfid = req[0];
658
            var oldname = req[1];
659
            var newdirfid = req[2];
660
            var newname = req[3];
661
            message.Debug("[renameat]: oldname=" + oldname + " newname=" + newname);
662
            var ret = await this.fs.Rename(this.fids[olddirfid].inodeid, oldname, this.fids[newdirfid].inodeid, newname);
663
            if (ret < 0) {
664
                let error_message = "";
665
                if(ret === -ENOENT) error_message = "No such file or directory";
666
                else if(ret === -EPERM) error_message = "Operation not permitted";
667
                else if(ret === -ENOTEMPTY) error_message = "Directory not empty";
668
                else
669
                {
670
                    error_message = "Unknown error: " + (-ret);
671
                    dbg_assert(false, "[renameat]: Unexpected error code: " + (-ret));
672
                }
673
                this.SendError(tag, error_message, -ret);
674
                this.SendReply(bufchain);
675
                break;
676
            }
677
            if(TRACK_FILENAMES)
678
            {
679
                const newidx = this.fs.Search(this.fids[newdirfid].inodeid, newname);
680
                this.update_dbg_name(newidx, newname);
681
            }
682
            this.BuildReply(id, tag, 0);
683
            this.SendReply(bufchain);
684
            break;
685

686
        case 76: // TUNLINKAT
687
            var req = marshall.Unmarshall(["w", "s", "w"], buffer, state);
688
            var dirfd = req[0];
689
            var name = req[1];
690
            var flags = req[2];
691
            message.Debug("[unlink]: dirfd=" + dirfd + " name=" + name + " flags=" + flags);
692
            var fid = this.fs.Search(this.fids[dirfd].inodeid, name);
693
            if (fid == -1) {
694
                   this.SendError(tag, "No such file or directory", ENOENT);
695
                   this.SendReply(bufchain);
696
                   break;
697
            }
698
            var ret = this.fs.Unlink(this.fids[dirfd].inodeid, name);
699
            if (ret < 0) {
700
                let error_message = "";
701
                if(ret === -ENOTEMPTY) error_message = "Directory not empty";
702
                else if(ret === -EPERM) error_message = "Operation not permitted";
703
                else
704
                {
705
                    error_message = "Unknown error: " + (-ret);
706
                    dbg_assert(false, "[unlink]: Unexpected error code: " + (-ret));
707
                }
708
                this.SendError(tag, error_message, -ret);
709
                this.SendReply(bufchain);
710
                break;
711
            }
712
            this.BuildReply(id, tag, 0);
713
            this.SendReply(bufchain);
714
            break;
715

716
        case 100: // version
717
            var version = marshall.Unmarshall(["w", "s"], buffer, state);
718
            message.Debug("[version]: msize=" + version[0] + " version=" + version[1]);
719
            this.msize = version[0];
720
            size = marshall.Marshall(["w", "s"], [this.msize, this.VERSION], this.replybuffer, 7);
721
            this.BuildReply(id, tag, size);
722
            this.SendReply(bufchain);
723
            break;
724

725
        case 104: // attach
726
            // return root directorie's QID
727
            var req = marshall.Unmarshall(["w", "w", "s", "s", "w"], buffer, state);
728
            var fid = req[0];
729
            var uid = req[4];
730
            message.Debug("[attach]: fid=" + fid + " afid=" + hex8(req[1]) + " uname=" + req[2] + " aname=" + req[3]);
731
            this.fids[fid] = this.Createfid(0, FID_INODE, uid, "");
732
            var inode = this.fs.GetInode(this.fids[fid].inodeid);
733
            marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
734
            this.BuildReply(id, tag, 13);
735
            this.SendReply(bufchain);
736
            break;
737

738
        case 108: // tflush
739
            var req = marshall.Unmarshall(["h"], buffer, state);
740
            var oldtag = req[0];
741
            message.Debug("[flush] " + tag);
742
            //marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
743
            this.BuildReply(id, tag, 0);
744
            this.SendReply(bufchain);
745
            break;
746

747

748
        case 110: // walk
749
            var req = marshall.Unmarshall(["w", "w", "h"], buffer, state);
750
            var fid = req[0];
751
            var nwfid = req[1];
752
            var nwname = req[2];
753
            message.Debug("[walk]: fid=" + req[0] + " nwfid=" + req[1] + " nwname=" + nwname);
754
            if (nwname == 0) {
755
                this.fids[nwfid] = this.Createfid(this.fids[fid].inodeid, FID_INODE, this.fids[fid].uid, this.fids[fid].dbg_name);
756
                //this.fids[nwfid].inodeid = this.fids[fid].inodeid;
757
                marshall.Marshall(["h"], [0], this.replybuffer, 7);
758
                this.BuildReply(id, tag, 2);
759
                this.SendReply(bufchain);
760
                break;
761
            }
762
            var wnames = [];
763
            for(var i=0; i<nwname; i++) {
764
                wnames.push("s");
765
            }
766
            var walk = marshall.Unmarshall(wnames, buffer, state);
767
            var idx = this.fids[fid].inodeid;
768
            var offset = 7+2;
769
            var nwidx = 0;
770
            //console.log(idx, this.fs.GetInode(idx));
771
            message.Debug("walk in dir " + this.fids[fid].dbg_name  + " to: " + walk.toString());
772
            for(var i=0; i<nwname; i++) {
773
                idx = this.fs.Search(idx, walk[i]);
774

775
                if (idx == -1) {
776
                   message.Debug("Could not find: " + walk[i]);
777
                   break;
778
                }
779
                offset += marshall.Marshall(["Q"], [this.fs.GetInode(idx).qid], this.replybuffer, offset);
780
                nwidx++;
781
                //message.Debug(this.fids[nwfid].inodeid);
782
                //this.fids[nwfid].inodeid = idx;
783
                //this.fids[nwfid].type = FID_INODE;
784
                this.fids[nwfid] = this.Createfid(idx, FID_INODE, this.fids[fid].uid, walk[i]);
785
            }
786
            marshall.Marshall(["h"], [nwidx], this.replybuffer, 7);
787
            this.BuildReply(id, tag, offset-7);
788
            this.SendReply(bufchain);
789
            break;
790

791
        case 120: // clunk
792
            var req = marshall.Unmarshall(["w"], buffer, state);
793
            message.Debug("[clunk]: fid=" + req[0]);
794
            if (this.fids[req[0]] && this.fids[req[0]].inodeid >=  0) {
795
                await this.fs.CloseInode(this.fids[req[0]].inodeid);
796
                this.fids[req[0]].inodeid = -1;
797
                this.fids[req[0]].type = FID_NONE;
798
            }
799
            this.BuildReply(id, tag, 0);
800
            this.SendReply(bufchain);
801
            break;
802

803
        case 32: // txattrcreate
804
            var req = marshall.Unmarshall(["w", "s", "d", "w"], buffer, state);
805
            var fid = req[0];
806
            var name = req[1];
807
            var attr_size = req[2];
808
            var flags = req[3];
809
            message.Debug("[txattrcreate]: fid=" + fid + " name=" + name + " attr_size=" + attr_size + " flags=" + flags);
810

811
            // XXX: xattr not supported yet. E.g. checks corresponding to the flags needed.
812
            this.fids[fid].type = FID_XATTR;
813

814
            this.BuildReply(id, tag, 0);
815
            this.SendReply(bufchain);
816
            //this.SendError(tag, "Operation i not supported",  EINVAL);
817
            //this.SendReply(bufchain);
818
            break;
819

820
        case 30: // xattrwalk
821
            var req = marshall.Unmarshall(["w", "w", "s"], buffer, state);
822
            var fid = req[0];
823
            var newfid = req[1];
824
            var name = req[2];
825
            message.Debug("[xattrwalk]: fid=" + req[0] + " newfid=" + req[1] + " name=" + req[2]);
826

827
            // Workaround for Linux restarts writes until full blocksize
828
            this.SendError(tag, "Setxattr not supported", EOPNOTSUPP);
829
            this.SendReply(bufchain);
830
            /*
831
            this.fids[newfid] = this.Createfid(this.fids[fid].inodeid, FID_NONE, this.fids[fid].uid, this.fids[fid].dbg_name);
832
            //this.fids[newfid].inodeid = this.fids[fid].inodeid;
833
            //this.fids[newfid].type = FID_NONE;
834
            var length = 0;
835
            if (name == "security.capability") {
836
                length = this.fs.PrepareCAPs(this.fids[fid].inodeid);
837
                this.fids[newfid].type = FID_XATTR;
838
            }
839
            marshall.Marshall(["d"], [length], this.replybuffer, 7);
840
            this.BuildReply(id, tag, 8);
841
            this.SendReply(bufchain);
842
            */
843
            break;
844

845
        default:
846
            message.Debug("Error in Virtio9p: Unknown id " + id + " received");
847
            message.Abort();
848
            //this.SendError(tag, "Operation i not supported",  EOPNOTSUPP);
849
            //this.SendReply(bufchain);
850
            break;
851
    }
852

853
    //consistency checks if there are problems with the filesystem
854
    //this.fs.Check();
855
};
856

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.