🍎
UnityでmacOSのファイルパーミッションを取得する
はじめに
macOS上のUniyからzipファイルを圧縮する際に、実行ファイルのパーミッションを反映する必要がありました。
C#は FileSystemInfo.UnixFileMode や Mono.Posix などのライブラリを使うことでファイルパーミッションを取得できますがUnityでは利用できませんでした。
そこで stat(2) システムコールを使ってmacOSのファイルパーミッションを取得する方法を調べました。
コード
#if UNITY_STANDALONE_OSX
using System;
using System.Runtime.InteropServices;
[Flags]
public enum Mode
{
SetUserId = 0b_100_000_000_000,
SetGroupId = 0b_010_000_000_000,
StickyBit = 0b_001_000_000_000,
ReadUser = 0b_000_100_000_000,
WriteUser = 0b_000_010_000_000,
ExecuteUser = 0b_000_001_000_000,
ReadGroup = 0b_000_000_100_000,
WriteGroup = 0b_000_000_010_000,
ExecuteGroup = 0b_000_000_001_000,
ReadOther = 0b_000_000_000_100,
WriteOther = 0b_000_000_000_010,
ExecuteOther = 0b_000_000_000_001,
}
public static class SysCall
{
[StructLayout(LayoutKind.Sequential)]
private struct Stat
{
public Int32 st_dev;
public UInt16 st_mode;
public UInt16 st_nlink;
public UInt64 st_no;
public ulong st_uid;
public uint st_gid;
public uint st_rdev;
public long st_atime;
public long st_atimensec;
public long st_mtime;
public long st_mtimensec;
public long st_ctime;
public long st_ctimensec;
public long st_birthtime;
public long st_birthtimensec;
public Int64 st_size;
public Int64 st_blocks;
public Int32 st_blocksize;
public UInt32 st_flags;
public UInt32 st_gen;
public UInt32 st_lspare;
public UInt64 st_qspare1;
public UInt64 st_qspare2;
}
[DllImport("libc", EntryPoint = "stat", SetLastError = true)]
private static extern int stat(string path, out Stat stat);
/// <summary>
/// ファイルのパーミッションを取得します
/// <param name="path">ファイルのパス</param>
/// <param name="mode">取得したパーミッション</param>
/// </summary>
public static bool TryGetUnixMode(string path, out Mode mode)
{
if (stat(path, out var stat) == 0)
{
mode = (Mode)(stat.st_mode & 0xFFF);
return true;
}
mode = 0;
return false;
}
}
#endif
テストコード兼使い方
#if UNITY_STANDALONE_OSX
using NUnit.Framework;
public class SysCallTest
{
[Test]
public void TestEtcPasswd1()
{
if (SysCall.TryGetUnixMode("/etc/passwd", out var mode))
{
Assert.AreEqual(mode, Mode.ReadUser | Mode.WriteUser | Mode.ReadGroup | Mode.ReadOther);
}
else
{
Assert.Fail();
}
}
[Test]
public void TestEtcPasswd2()
{
if (SysCall.TryGetUnixMode("/etc/passwd", out var mode))
{
Assert.AreEqual((int)mode, 0b_000_110_100_100);
}
else
{
Assert.Fail();
}
}
[Test]
public void TestShBin1()
{
if (SysCall.TryGetUnixMode("/bin/sh", out var mode))
{
Assert.AreEqual(mode,
Mode.ReadUser | Mode.WriteUser | Mode.ExecuteUser |
Mode.ReadGroup | Mode.ExecuteGroup |
Mode.ReadOther | Mode.EcecuteOther);
}
else
{
Assert.Fail();
}
}
[Test]
public void TestShBin2()
{
if (SysCall.TryGetUnixMode("/bin/sh", out var mode))
{
Assert.AreEqual((int)mode, 0b_000_111_101_101);
}
else
{
Assert.Fail();
}
}
}
#endif
最後に
man 2 stat64
でstat構造体が分かりますが、 dev_t
や mode_t
などの具体的な型が分からないため、find /Applications/Xcode.app -name stat.h -type f
でXcodeのヘッダーファイルを探してすべての型を調べました。
Discussion