File: Open.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (101 lines) | stat: -rw-r--r-- 3,320 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import SystemExtras
import SystemPackage

struct PathResolution {
    private let mode: FileDescriptor.AccessMode
    private let options: FileDescriptor.OpenOptions
    private let permissions: FilePermissions

    private var baseFd: FileDescriptor
    private let path: FilePath
    private var openDirectories: [FileDescriptor]
    /// Reverse-ordered remaining path components
    private var components: FilePath.ComponentView

    init(
        baseDirFd: FileDescriptor,
        mode: FileDescriptor.AccessMode,
        options: FileDescriptor.OpenOptions,
        permissions: FilePermissions,
        path: FilePath
    ) {
        self.baseFd = baseDirFd
        self.mode = mode
        self.options = options
        self.permissions = permissions
        self.path = path
        self.openDirectories = []
        self.components = FilePath.ComponentView(path.components.reversed())
    }

    mutating func parentDirectory() throws {
        guard let lastDirectory = openDirectories.popLast() else {
            // no more parent directory means too many `..`
            throw WASIAbi.Errno.EPERM
        }
        self.baseFd = lastDirectory
    }

    mutating func regular(component: FilePath.Component) throws {
        let options: FileDescriptor.OpenOptions
        let mode: FileDescriptor.AccessMode
        if !self.components.isEmpty {
            var intermediateOtions: FileDescriptor.OpenOptions = []

            // When trying to open an intermediate directory,
            // we can assume it's directory.
            intermediateOtions.insert(.directory)
            // FIXME: Resolve symlink in safe way
            intermediateOtions.insert(.noFollow)
            options = intermediateOtions
            mode = .readOnly
        } else {
            options = self.options
            mode = self.mode
        }

        try WASIAbi.Errno.translatingPlatformErrno {
            let newFd = try self.baseFd.open(
                at: FilePath(root: nil, components: component),
                mode, options: options, permissions: permissions
            )
            self.openDirectories.append(self.baseFd)
            self.baseFd = newFd
        }
    }

    mutating func resolve() throws -> FileDescriptor {
        if path.isAbsolute {
            // POSIX openat(2) interprets absolute path ignoring base directory fd
            // but it leads sandbox-escaping, so reject absolute path here.
            throw WASIAbi.Errno.EPERM
        }

        while let component = components.popLast() {
            switch component.kind {
            case .currentDirectory:
                break  // no-op
            case .parentDirectory:
                try parentDirectory()
            case .regular: try regular(component: component)
            }
        }
        return self.baseFd
    }
}

extension SandboxPrimitives {
    static func openAt(
        start startFd: FileDescriptor,
        path: FilePath,
        mode: FileDescriptor.AccessMode,
        options: FileDescriptor.OpenOptions,
        permissions: FilePermissions
    ) throws -> FileDescriptor {
        var resolution = PathResolution(
            baseDirFd: startFd, mode: mode, options: options,
            permissions: permissions, path: path
        )
        return try resolution.resolve()
    }
}