authorgravatar for 37453713+Ominitay@users.noreply.github.comOminitay <37453713+Ominitay@users.noreply.github.com> 2021-11-05 15:19:49+00:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-15 20:04:55-05:00
log796687f1562bf0c9a73633b3fd828d25b7a11fac
tree9b6d2ca4e98f41f2410043e3ed74ad7cb5af5362
parentdfd604834b9bda7ba5cb8117a233ae51bcbc8c0f

Add `chmod` and `chown`


6 files changed, 185 insertions(+), 0 deletions(-)

lib/std/c.zig+2
......@@ -147,6 +147,8 @@ pub extern "c" fn dup(fd: c.fd_t) c_int;
147147pub extern "c" fn dup2(old_fd: c.fd_t, new_fd: c.fd_t) c_int;
148148pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;
149149pub extern "c" fn readlinkat(dirfd: c.fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;
150pub extern "c" fn fchmod(fd: c.fd_t, mode: c.mode_t) c_int;
151pub extern "c" fn fchown(fd: c.fd_t, owner: c.uid_t, group: c.gid_t) c_int;
150152
151153pub extern "c" fn rmdir(path: [*:0]const u8) c_int;
152154pub extern "c" fn getenv(name: [*:0]const u8) ?[*:0]u8;
lib/std/fs.zig+31
......@@ -2178,6 +2178,37 @@ pub const Dir = struct {
21782178 };
21792179 return file.stat();
21802180 }
2181
2182 pub const ChmodError = File.ChmodError;
2183
2184 /// Changes the mode of the directory.
2185 /// The process must have the correct privileges in order to do this
2186 /// successfully, or must have the effective user ID matching the owner
2187 /// of the directory. Additionally, the directory must have been opened
2188 /// with `OpenDirOptions{ .iterate = true }`.
2189 pub fn chmod(self: Dir, new_mode: File.Mode) ChmodError!void {
2190 const file: File = .{
2191 .handle = self.fd,
2192 .capable_io_mode = .blocking,
2193 };
2194 try file.chmod(new_mode);
2195 }
2196
2197 /// Changes the owner and group of the directory.
2198 /// The process must have the correct privileges in order to do this
2199 /// successfully. The group may be changed by the owner of the directory to
2200 /// any group of which the owner is a member. Additionally, the directory
2201 /// must have been opened with `OpenDirOptions{ .iterate = true }`. If the
2202 /// owner or group is specified as `null`, the ID is not changed.
2203 pub fn chown(self: Dir, owner: ?File.Uid, group: ?File.Gid) ChownError!void {
2204 const file: File = .{
2205 .handle = self.fd,
2206 .capable_io_mode = .blocking,
2207 };
2208 try file.chown(owner, group);
2209 }
2210
2211 pub const ChownError = File.ChownError;
21812212};
21822213
21832214/// Returns a handle to the current working directory. It is not opened with iteration capability.
lib/std/fs/file.zig+23
......@@ -31,6 +31,8 @@ pub const File = struct {
3131 pub const Handle = os.fd_t;
3232 pub const Mode = os.mode_t;
3333 pub const INode = os.ino_t;
34 pub const Uid = os.uid_t;
35 pub const Gid = os.gid_t;
3436
3537 pub const Kind = enum {
3638 BlockDevice,
......@@ -362,6 +364,27 @@ pub const File = struct {
362364 };
363365 }
364366
367 pub const ChmodError = std.os.FChmodError;
368
369 /// Changes the mode of the file.
370 /// The process must have the correct privileges in order to do this
371 /// successfully, or must have the effective user ID matching the owner
372 /// of the file.
373 pub fn chmod(self: File, new_mode: Mode) ChmodError!void {
374 try os.fchmod(self.handle, new_mode);
375 }
376
377 pub const ChownError = std.os.FChownError;
378
379 /// Changes the owner and group of the file.
380 /// The process must have the correct privileges in order to do this
381 /// successfully. The group may be changed by the owner of the file to
382 /// any group of which the owner is a member. If the owner or group is
383 /// specified as `null`, the ID is not changed.
384 pub fn chown(self: File, owner: ?Uid, group: ?Gid) ChownError!void {
385 try os.fchown(self.handle, owner, group);
386 }
387
365388 pub const UpdateTimesError = os.FutimensError || windows.SetFileTimeError;
366389
367390 /// The underlying file system may have a different granularity than nanoseconds,
lib/std/fs/test.zig+40
......@@ -1022,3 +1022,43 @@ test ". and .. in absolute functions" {
10221022
10231023 try fs.deleteDirAbsolute(subdir_path);
10241024}
1025
1026test "chmod" {
1027 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
1028 return error.SkipZigTest;
1029
1030 var tmp = tmpDir(.{});
1031 defer tmp.cleanup();
1032
1033 const file = try tmp.dir.createFile("test_file", .{ .mode = 0o600 });
1034 defer file.close();
1035 try testing.expect((try file.stat()).mode & 0o7777 == 0o600);
1036
1037 try file.chmod(0o644);
1038 try testing.expect((try file.stat()).mode & 0o7777 == 0o644);
1039
1040 try tmp.dir.makeDir("test_dir");
1041 var dir = try tmp.dir.openDir("test_dir", .{ .iterate = true });
1042 defer dir.close();
1043
1044 try dir.chmod(0o700);
1045 try testing.expect((try dir.stat()).mode & 0o7777 == 0o700);
1046}
1047
1048test "chown" {
1049 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
1050 return error.SkipZigTest;
1051
1052 var tmp = tmpDir(.{});
1053 defer tmp.cleanup();
1054
1055 const file = try tmp.dir.createFile("test_file", .{});
1056 defer file.close();
1057 try file.chown(null, null);
1058
1059 try tmp.dir.makeDir("test_dir");
1060
1061 var dir = try tmp.dir.openDir("test_dir", .{ .iterate = true });
1062 defer dir.close();
1063 try dir.chown(null, null);
1064}
lib/std/os.zig+81
......@@ -255,6 +255,87 @@ pub fn close(fd: fd_t) void {
255255 }
256256}
257257
258pub const FChmodError = error{
259 AccessDenied,
260 InputOutput,
261 SymLinkLoop,
262 FileNotFound,
263 SystemResources,
264 ReadOnlyFileSystem,
265} || UnexpectedError;
266
267/// Changes the mode of the file referred to by the file descriptor.
268/// The process must have the correct privileges in order to do this
269/// successfully, or must have the effective user ID matching the owner
270/// of the file.
271pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void {
272 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
273 @compileError("Unsupported OS");
274
275 while (true) {
276 const res = system.fchmod(fd, mode);
277
278 switch (system.getErrno(res)) {
279 .SUCCESS => return,
280 .INTR => continue,
281 .BADF => unreachable, // Can be reached if the fd refers to a directory opened without `OpenDirOptions{ .iterate = true }`
282
283 .FAULT => unreachable,
284 .INVAL => unreachable,
285 .ACCES => return error.AccessDenied,
286 .IO => return error.InputOutput,
287 .LOOP => return error.SymLinkLoop,
288 .NOENT => return error.FileNotFound,
289 .NOMEM => return error.SystemResources,
290 .NOTDIR => return error.FileNotFound,
291 .PERM => return error.AccessDenied,
292 .ROFS => return error.ReadOnlyFileSystem,
293 else => |err| return unexpectedErrno(err),
294 }
295 }
296}
297
298pub const FChownError = error{
299 AccessDenied,
300 InputOutput,
301 SymLinkLoop,
302 FileNotFound,
303 SystemResources,
304 ReadOnlyFileSystem,
305} || UnexpectedError;
306
307/// Changes the owner and group of the file referred to by the file descriptor.
308/// The process must have the correct privileges in order to do this
309/// successfully. The group may be changed by the owner of the directory to
310/// any group of which the owner is a member. If the owner or group is
311/// specified as `null`, the ID is not changed.
312pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void {
313 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
314 @compileError("Unsupported OS");
315
316 while (true) {
317 const res = system.fchown(fd, owner orelse @as(u32, 0) -% 1, group orelse @as(u32, 0) -% 1);
318
319 switch (system.getErrno(res)) {
320 .SUCCESS => return,
321 .INTR => continue,
322 .BADF => unreachable, // Can be reached if the fd refers to a directory opened without `OpenDirOptions{ .iterate = true }`
323
324 .FAULT => unreachable,
325 .INVAL => unreachable,
326 .ACCES => return error.AccessDenied,
327 .IO => return error.InputOutput,
328 .LOOP => return error.SymLinkLoop,
329 .NOENT => return error.FileNotFound,
330 .NOMEM => return error.SystemResources,
331 .NOTDIR => return error.FileNotFound,
332 .PERM => return error.AccessDenied,
333 .ROFS => return error.ReadOnlyFileSystem,
334 else => |err| return unexpectedErrno(err),
335 }
336 }
337}
338
258339pub const GetRandomError = OpenError;
259340
260341/// Obtain a series of random bytes. These bytes can be used to seed user-space
lib/std/os/linux.zig+8
......@@ -733,6 +733,14 @@ pub fn close(fd: i32) usize {
733733 return syscall1(.close, @bitCast(usize, @as(isize, fd)));
734734}
735735
736pub fn fchmod(fd: i32, mode: mode_t) usize {
737 return syscall2(.fchmod, @bitCast(usize, @as(isize, fd)), mode);
738}
739
740pub fn fchown(fd: i32, owner: uid_t, group: gid_t) usize {
741 return syscall3(.fchown, @bitCast(usize, @as(isize, fd)), owner, group);
742}
743
736744/// Can only be called on 32 bit systems. For 64 bit see `lseek`.
737745pub fn llseek(fd: i32, offset: u64, result: ?*u64, whence: usize) usize {
738746 // NOTE: The offset parameter splitting is independent from the target