File: Directory.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 (169 lines) | stat: -rw-r--r-- 6,380 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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import SystemPackage

struct DirEntry {
    let preopenPath: String?
    let fd: FileDescriptor
}

extension DirEntry: WASIDir, FdWASIEntry {
    func openFile(
        symlinkFollow: Bool,
        path: String,
        oflags: WASIAbi.Oflags,
        accessMode: FileAccessMode,
        fdflags: WASIAbi.Fdflags
    ) throws -> FileDescriptor {
        var options: FileDescriptor.OpenOptions = []
        if !symlinkFollow {
            options.insert(.noFollow)
        }

        if oflags.contains(.DIRECTORY) {
            options.insert(.directory)
        } else {
            // For regular file
            if oflags.contains(.CREAT) {
                options.insert(.create)
            }
            if oflags.contains(.EXCL) {
                options.insert(.exclusiveCreate)
            }
            if oflags.contains(.TRUNC) {
                options.insert(.truncate)
            }
        }

        // SystemPackage.FilePath implicitly normalizes the trailing "/", however
        // it means the last component is expected to be a directory. Therefore
        // check it here before converting path string to FilePath.
        if path.hasSuffix("/") {
            options.insert(.directory)
        }

        if fdflags.contains(.APPEND) {
            options.insert(.append)
        }

        let mode: FileDescriptor.AccessMode
        switch (accessMode.contains(.read), accessMode.contains(.write)) {
        case (true, true): mode = .readWrite
        case (true, false): mode = .readOnly
        case (false, true): mode = .writeOnly
        case (false, false):
            // If not opened for neither write nor read, set read mode by default
            // because underlying `openat` requires mode but WASI's
            // `path_open` can omit FD_READ.
            // https://man7.org/linux/man-pages/man2/open.2.html
            // > The argument flags must include one of the following access
            // > modes: O_RDONLY, O_WRONLY, or O_RDWR.  These request opening the
            // > file read-only, write-only, or read/write, respectively.
            mode = .readOnly
        }

        let newFd = try SandboxPrimitives.openAt(
            start: self.fd,
            path: FilePath(path), mode: mode, options: options,
            // Use 0o600 open mode as the minimum permission
            permissions: .ownerReadWrite
        )
        return newFd
    }

    func setFilestatTimes(
        path: String,
        atim: WASIAbi.Timestamp, mtim: WASIAbi.Timestamp,
        fstFlags: WASIAbi.FstFlags, symlinkFollow: Bool
    ) throws {
        let fd = try openFile(
            symlinkFollow: symlinkFollow, path: path,
            oflags: [], accessMode: .write, fdflags: []
        )
        let (access, modification) = try WASIAbi.Timestamp.platformTimeSpec(
            atim: atim, mtim: mtim, fstFlags: fstFlags
        )
        try WASIAbi.Errno.translatingPlatformErrno {
            try fd.setTimes(access: access, modification: modification)
        }
    }

    func removeFile(atPath path: String) throws {
        let (dir, basename) = try SandboxPrimitives.openParent(start: fd, path: path)
        try WASIAbi.Errno.translatingPlatformErrno {
            try dir.remove(at: FilePath(basename), options: [])
        }
    }

    func removeDirectory(atPath path: String) throws {
        let (dir, basename) = try SandboxPrimitives.openParent(start: fd, path: path)
        try WASIAbi.Errno.translatingPlatformErrno {
            try dir.remove(at: FilePath(basename), options: .removeDirectory)
        }
    }

    func symlink(from sourcePath: String, to destPath: String) throws {
        let (destDir, destBasename) = try SandboxPrimitives.openParent(
            start: fd, path: destPath
        )
        try WASIAbi.Errno.translatingPlatformErrno {
            try destDir.createSymlink(original: FilePath(sourcePath), link: FilePath(destBasename))
        }
    }

    func readEntries(
        cookie: WASIAbi.DirCookie
    ) throws -> AnyIterator<Result<ReaddirElement, any Error>> {
        // Duplicate fd because readdir takes the ownership of
        // the given fd and closedir also close the underlying fd
        let newFd = try WASIAbi.Errno.translatingPlatformErrno {
            try fd.open(at: ".", .readOnly, options: [])
        }
        let iterator = try WASIAbi.Errno.translatingPlatformErrno {
            try newFd.contentsOfDirectory()
        }
        .lazy.enumerated()
        .map { (entryIndex, entry) in
            return Result(catching: { () -> ReaddirElement in
                let entry = try entry.get()
                let name = entry.name
                let stat = try WASIAbi.Errno.translatingPlatformErrno {
                    try fd.attributes(at: name, options: [])
                }
                let dirent = WASIAbi.Dirent(
                    // We can't use telldir and seekdir because the location data
                    // is valid for only the same dirp but and there is no way to
                    // share dirp among fd_readdir calls.
                    dNext: WASIAbi.DirCookie(entryIndex + 1),
                    dIno: stat.inode,
                    dirNameLen: WASIAbi.DirNameLen(name.utf8.count),
                    dType: WASIAbi.FileType(platformFileType: entry.fileType)
                )
                return (dirent, name)
            })
        }
        .dropFirst(Int(cookie))
        .makeIterator()
        return AnyIterator(iterator)
    }

    func createDirectory(atPath path: String) throws {
        let (dir, basename) = try SandboxPrimitives.openParent(start: fd, path: path)
        try WASIAbi.Errno.translatingPlatformErrno {
            try dir.createDirectory(at: FilePath(basename), permissions: .ownerReadWriteExecute)
        }
    }

    func attributes(path: String, symlinkFollow: Bool) throws -> WASIAbi.Filestat {
        var options: FileDescriptor.AtOptions = []
        if !symlinkFollow {
            options.insert(.noFollow)
        }
        let (dir, basename) = try SandboxPrimitives.openParent(start: fd, path: path)
        let attributes = try basename.withCString { cBasename in
            try WASIAbi.Errno.translatingPlatformErrno {
                try dir.attributes(at: cBasename, options: options)
            }
        }

        return WASIAbi.Filestat(stat: attributes)
    }
}