| ... | @@ -2091,7 +2091,7 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { | ... | @@ -2091,7 +2091,7 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 2091 | try current_thread.beginSyscall(); | 2091 | try current_thread.beginSyscall(); |
| 2092 | while (true) { | 2092 | while (true) { |
| 2093 | var statx = std.mem.zeroes(linux.Statx); | 2093 | var statx = std.mem.zeroes(linux.Statx); |
| 2094 | const rc = sys.statx( | 2094 | switch (sys.errno(sys.statx( |
| 2095 | file.handle, | 2095 | file.handle, |
| 2096 | "", | 2096 | "", |
| 2097 | linux.AT.EMPTY_PATH, | 2097 | linux.AT.EMPTY_PATH, |
| ... | @@ -2106,8 +2106,7 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { | ... | @@ -2106,8 +2106,7 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 2106 | .NLINK = true, | 2106 | .NLINK = true, |
| 2107 | }, | 2107 | }, |
| 2108 | &statx, | 2108 | &statx, |
| 2109 | ); | 2109 | ))) { |
| 2110 | switch (sys.errno(rc)) { | | |
| 2111 | .SUCCESS => { | 2110 | .SUCCESS => { |
| 2112 | current_thread.endSyscall(); | 2111 | current_thread.endSyscall(); |
| 2113 | return statFromLinux(&statx); | 2112 | return statFromLinux(&statx); |
| ... | @@ -5120,7 +5119,7 @@ fn posixFchmodat( | ... | @@ -5120,7 +5119,7 @@ fn posixFchmodat( |
| 5120 | } | 5119 | } |
| 5121 | | 5120 | |
| 5122 | if (@atomicLoad(UseFchmodat2, &t.use_fchmodat2, .monotonic) == .disabled) | 5121 | if (@atomicLoad(UseFchmodat2, &t.use_fchmodat2, .monotonic) == .disabled) |
| 5123 | return fchmodatFallback(current_thread, dir_fd, path, mode, flags); | 5122 | return fchmodatFallback(current_thread, dir_fd, path, mode); |
| 5124 | | 5123 | |
| 5125 | comptime assert(native_os == .linux); | 5124 | comptime assert(native_os == .linux); |
| 5126 | | 5125 | |
| ... | @@ -5149,7 +5148,7 @@ fn posixFchmodat( | ... | @@ -5149,7 +5148,7 @@ fn posixFchmodat( |
| 5149 | .ROFS => return error.ReadOnlyFileSystem, | 5148 | .ROFS => return error.ReadOnlyFileSystem, |
| 5150 | .NOSYS => { | 5149 | .NOSYS => { |
| 5151 | @atomicStore(UseFchmodat2, &t.use_fchmodat2, .disabled, .monotonic); | 5150 | @atomicStore(UseFchmodat2, &t.use_fchmodat2, .disabled, .monotonic); |
| 5152 | return fchmodatFallback(current_thread, dir_fd, path, mode, flags); | 5151 | return fchmodatFallback(current_thread, dir_fd, path, mode); |
| 5153 | }, | 5152 | }, |
| 5154 | else => |err| return posix.unexpectedErrno(err), | 5153 | else => |err| return posix.unexpectedErrno(err), |
| 5155 | } | 5154 | } |
| ... | @@ -5163,16 +5162,114 @@ fn fchmodatFallback( | ... | @@ -5163,16 +5162,114 @@ fn fchmodatFallback( |
| 5163 | dir_fd: posix.fd_t, | 5162 | dir_fd: posix.fd_t, |
| 5164 | path: [*:0]const u8, | 5163 | path: [*:0]const u8, |
| 5165 | mode: posix.mode_t, | 5164 | mode: posix.mode_t, |
| 5166 | flags: u32, | | |
| 5167 | ) Dir.SetFilePermissionsError!void { | 5165 | ) Dir.SetFilePermissionsError!void { |
| 5168 | _ = current_thread; | 5166 | comptime assert(native_os == .linux); |
| 5169 | _ = dir_fd; | 5167 | const use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) |
| 5170 | _ = path; | 5168 | .{ .major = 30, .minor = 0, .patch = 0 } |
| 5171 | _ = mode; | 5169 | else |
| 5172 | _ = flags; | 5170 | .{ .major = 2, .minor = 28, .patch = 0 }); |
| 5173 | // I deleted the previous fallback implementation because it looked wrong to me. Please cross-reference | 5171 | const sys = if (use_c) std.c else std.os.linux; |
| 5174 | // fhmodat.c in musl libc before blindly restoring the implementation. | 5172 | |
| 5175 | @panic("TODO"); | 5173 | // Fallback to changing permissions using procfs: |
| | 5174 | // |
| | 5175 | // 1. Open `path` as a `PATH` descriptor. |
| | 5176 | // 2. Stat the fd and check if it isn't a symbolic link. |
| | 5177 | // 3. Generate the procfs reference to the fd via `/proc/self/fd/{fd}`. |
| | 5178 | // 4. Pass the procfs path to `chmod` with the `mode`. |
| | 5179 | try current_thread.beginSyscall(); |
| | 5180 | const path_fd: posix.fd_t = while (true) { |
| | 5181 | const rc = posix.system.openat(dir_fd, path, .{ |
| | 5182 | .PATH = true, |
| | 5183 | .NOFOLLOW = true, |
| | 5184 | .CLOEXEC = true, |
| | 5185 | }, @as(posix.mode_t, 0)); |
| | 5186 | switch (posix.errno(rc)) { |
| | 5187 | .SUCCESS => { |
| | 5188 | current_thread.endSyscall(); |
| | 5189 | break @intCast(rc); |
| | 5190 | }, |
| | 5191 | .INTR => { |
| | 5192 | try current_thread.checkCancel(); |
| | 5193 | continue; |
| | 5194 | }, |
| | 5195 | else => |e| { |
| | 5196 | current_thread.endSyscall(); |
| | 5197 | switch (e) { |
| | 5198 | .FAULT => |err| return errnoBug(err), |
| | 5199 | .INVAL => |err| return errnoBug(err), |
| | 5200 | .ACCES => return error.AccessDenied, |
| | 5201 | .PERM => return error.PermissionDenied, |
| | 5202 | .LOOP => return error.SymLinkLoop, |
| | 5203 | .MFILE => return error.ProcessFdQuotaExceeded, |
| | 5204 | .NAMETOOLONG => return error.NameTooLong, |
| | 5205 | .NFILE => return error.SystemFdQuotaExceeded, |
| | 5206 | .NOENT => return error.FileNotFound, |
| | 5207 | .NOMEM => return error.SystemResources, |
| | 5208 | else => |err| return posix.unexpectedErrno(err), |
| | 5209 | } |
| | 5210 | }, |
| | 5211 | } |
| | 5212 | }; |
| | 5213 | defer posix.close(path_fd); |
| | 5214 | |
| | 5215 | try current_thread.beginSyscall(); |
| | 5216 | const path_mode = while (true) { |
| | 5217 | var statx = std.mem.zeroes(std.os.linux.Statx); |
| | 5218 | switch (sys.errno(sys.statx(path_fd, "", posix.AT.EMPTY_PATH, .{ .TYPE = true }, &statx))) { |
| | 5219 | .SUCCESS => { |
| | 5220 | current_thread.endSyscall(); |
| | 5221 | assert(statx.mask.TYPE); |
| | 5222 | break statx.mode; |
| | 5223 | }, |
| | 5224 | .INTR => { |
| | 5225 | try current_thread.checkCancel(); |
| | 5226 | continue; |
| | 5227 | }, |
| | 5228 | else => |e| { |
| | 5229 | current_thread.endSyscall(); |
| | 5230 | switch (e) { |
| | 5231 | .ACCES => return error.AccessDenied, |
| | 5232 | .LOOP => return error.SymLinkLoop, |
| | 5233 | .NOMEM => return error.SystemResources, |
| | 5234 | else => |err| return posix.unexpectedErrno(err), |
| | 5235 | } |
| | 5236 | }, |
| | 5237 | } |
| | 5238 | }; |
| | 5239 | |
| | 5240 | // Even though we only wanted TYPE, the kernel can still fill in the additional bits. |
| | 5241 | if ((path_mode & posix.S.IFMT) == posix.S.IFLNK) |
| | 5242 | return error.OperationUnsupported; |
| | 5243 | |
| | 5244 | var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; |
| | 5245 | const proc_path = std.fmt.bufPrintSentinel(&procfs_buf, "/proc/self/fd/{d}", .{path_fd}, 0) catch unreachable; |
| | 5246 | try current_thread.beginSyscall(); |
| | 5247 | while (true) { |
| | 5248 | switch (posix.errno(posix.system.chmod(proc_path, mode))) { |
| | 5249 | .SUCCESS => return current_thread.endSyscall(), |
| | 5250 | .INTR => { |
| | 5251 | try current_thread.checkCancel(); |
| | 5252 | continue; |
| | 5253 | }, |
| | 5254 | else => |e| { |
| | 5255 | current_thread.endSyscall(); |
| | 5256 | switch (e) { |
| | 5257 | .NOENT => return error.OperationUnsupported, // procfs not mounted. |
| | 5258 | .BADF => |err| return errnoBug(err), |
| | 5259 | .FAULT => |err| return errnoBug(err), |
| | 5260 | .INVAL => |err| return errnoBug(err), |
| | 5261 | .ACCES => return error.AccessDenied, |
| | 5262 | .IO => return error.InputOutput, |
| | 5263 | .LOOP => return error.SymLinkLoop, |
| | 5264 | .NOMEM => return error.SystemResources, |
| | 5265 | .NOTDIR => return error.FileNotFound, |
| | 5266 | .PERM => return error.PermissionDenied, |
| | 5267 | .ROFS => return error.ReadOnlyFileSystem, |
| | 5268 | else => |err| return posix.unexpectedErrno(err), |
| | 5269 | } |
| | 5270 | }, |
| | 5271 | } |
| | 5272 | } |
| 5176 | } | 5273 | } |
| 5177 | | 5274 | |
| 5178 | const dirSetOwner = switch (native_os) { | 5275 | const dirSetOwner = switch (native_os) { |