| ... | ... | @@ -351,7 +351,6 @@ const FChmodAtError = FChmodError || error{ |
| 351 | 351 | /// A component of `path` exceeded `NAME_MAX`, or the entire path exceeded |
| 352 | 352 | /// `PATH_MAX`. |
| 353 | 353 | NameTooLong, |
| 354 | | |
| 355 | 354 | /// `path` resolves to a symbolic link, and `AT.SYMLINK_NOFOLLOW` was set |
| 356 | 355 | /// in `flags`. This error only occurs on Linux, where changing the mode of |
| 357 | 356 | /// a symbolic link has no meaning and can cause undefined behaviour on |
| ... | ... | @@ -359,23 +358,15 @@ const FChmodAtError = FChmodError || error{ |
| 359 | 358 | /// |
| 360 | 359 | /// The procfs fallback was used but procfs was not mounted. |
| 361 | 360 | OperationNotSupported, |
| 362 | | |
| 363 | 361 | /// The procfs fallback was used but the process exceeded its open file |
| 364 | 362 | /// limit. |
| 365 | 363 | ProcessFdQuotaExceeded, |
| 366 | | |
| 367 | 364 | /// The procfs fallback was used but the system exceeded it open file limit. |
| 368 | 365 | SystemFdQuotaExceeded, |
| 369 | 366 | }; |
| 370 | 367 | |
| 371 | 368 | var has_fchmodat2_syscall = std.atomic.Value(bool).init(true); |
| 372 | 369 | |
| 373 | | inline fn skipFchmodatFallback(flags: u32) bool { |
| 374 | | return builtin.os.tag != .linux or |
| 375 | | flags == 0 or |
| 376 | | std.c.versionCheck(std.SemanticVersion{ .major = 2, .minor = 32, .patch = 0 }).ok; |
| 377 | | } |
| 378 | | |
| 379 | 370 | /// Changes the `mode` of `path` relative to the directory referred to by |
| 380 | 371 | /// `dirfd`. The process must have the correct privileges in order to do this |
| 381 | 372 | /// successfully, or must have the effective user ID matching the owner of the |
| ... | ... | @@ -394,11 +385,23 @@ inline fn skipFchmodatFallback(flags: u32) bool { |
| 394 | 385 | pub inline fn fchmodat(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void { |
| 395 | 386 | if (!std.fs.has_executable_bit) @compileError("fchmodat unsupported by target OS"); |
| 396 | 387 | |
| 397 | | const path_c = try toPosixPath(path); |
| 398 | | |
| 399 | 388 | // No special handling for linux is needed if we can use the libc fallback |
| 400 | 389 | // or `flags` is empty. Glibc only added the fallback in 2.32. |
| 401 | | while (skipFchmodatFallback(flags)) { |
| 390 | const skip_fchmodat_fallback = builtin.os.tag != .linux or |
| 391 | std.c.versionCheck(.{ .major = 2, .minor = 32, .patch = 0 }) or |
| 392 | flags == 0; |
| 393 | |
| 394 | // This function is marked inline so that when flags is comptime-known, |
| 395 | // skip_fchmodat_fallback will be comptime-known true. |
| 396 | if (skip_fchmodat_fallback) |
| 397 | return fchmodat1(dirfd, path, mode, flags); |
| 398 | |
| 399 | return fchmodat2(dirfd, path, mode, flags); |
| 400 | } |
| 401 | |
| 402 | fn fchmodat1(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void { |
| 403 | const path_c = try toPosixPath(path); |
| 404 | while (true) { |
| 402 | 405 | const res = system.fchmodat(dirfd, &path_c, mode, flags); |
| 403 | 406 | switch (system.getErrno(res)) { |
| 404 | 407 | .SUCCESS => return, |
| ... | ... | @@ -421,8 +424,11 @@ pub inline fn fchmodat(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) |
| 421 | 424 | else => |err| return unexpectedErrno(err), |
| 422 | 425 | } |
| 423 | 426 | } |
| 427 | } |
| 424 | 428 | |
| 425 | | const use_fchmodat2 = (comptime builtin.os.isAtLeast(.linux, .{ .major = 6, .minor = 6, .patch = 0 }) orelse false) and |
| 429 | fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void { |
| 430 | const path_c = try toPosixPath(path); |
| 431 | const use_fchmodat2 = (builtin.os.isAtLeast(.linux, .{ .major = 6, .minor = 6, .patch = 0 }) orelse false) and |
| 426 | 432 | has_fchmodat2_syscall.load(.Monotonic); |
| 427 | 433 | while (use_fchmodat2) { |
| 428 | 434 | // Later on this should be changed to `system.fchmodat2` |