diff --git a/Claunia.IO/ChangeLog b/Claunia.IO/ChangeLog index 8e7aac9..773b77a 100644 --- a/Claunia.IO/ChangeLog +++ b/Claunia.IO/ChangeLog @@ -1,3 +1,24 @@ +2015-02-12 Natalia Portillo + + * Interop/Apple/Interop.Apple.Errors.cs: + Added all errors defined by BSD layer of OS X. + + * Tests/Interop.Apple.cs: + Added tests for Apple's stat() and stat64() + + * packages.config: + Added NUnit to do tests. + + * Claunia.IO.csproj: + Added tests + + * Interop/Apple/Interop.Apple.stat.cs: + Mono calls stat with 32-bit inode, modify structure and add + stat64() call. + + * Interop/Apple/Interop.Apple.types.cs: + Pack structure, dunno if needed + 2015-02-12 Natalia Portillo * Claunia.IO.csproj: diff --git a/Claunia.IO/Claunia.IO.csproj b/Claunia.IO/Claunia.IO.csproj index 305b422..3958474 100644 --- a/Claunia.IO/Claunia.IO.csproj +++ b/Claunia.IO/Claunia.IO.csproj @@ -29,6 +29,9 @@ + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + @@ -40,15 +43,19 @@ + + LICENSE + + \ No newline at end of file diff --git a/Claunia.IO/Interop/Apple/Interop.Apple.Errors.cs b/Claunia.IO/Interop/Apple/Interop.Apple.Errors.cs new file mode 100644 index 0000000..8115110 --- /dev/null +++ b/Claunia.IO/Interop/Apple/Interop.Apple.Errors.cs @@ -0,0 +1,270 @@ +// +// Interop.Apple.Errors.cs +// +// Author: +// Natalia Portillo +// +// Copyright (c) 2015 © Claunia.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +internal static partial class Interop +{ + internal static partial class Apple + { + internal enum Errors + { + /// Operation not permitted + EPERM = 1, + /// No such file or directory + ENOENT = 2, + /// No such process + ESRCH = 3, + /// Interrupted system call + EINTR = 4, + /// Input/output error + EIO = 5, + /// Device not configured + ENXIO = 6, + /// Argument list too long + E2BIG = 7, + /// Exec format error + ENOEXEC = 8, + /// Bad file descriptor + EBADF = 9, + /// No child processes + ECHILD = 10, + /// Resource deadlock avoided + EDEADLK = 11, + /// Cannot allocate memory + ENOMEM = 12, + /// Permission denied + EACCES = 13, + /// Bad address + EFAULT = 14, + /// Block device required + ENOTBLK = 15, + /// Device / Resource busy + EBUSY = 16, + /// File exists + EEXIST = 17, + /// Cross-device link + EXDEV = 18, + /// Operation not supported by device + ENODEV = 19, + /// Not a directory + ENOTDIR = 20, + /// Is a directory + EISDIR = 21, + /// Invalid argument + EINVAL = 22, + /// Too many open files in system + ENFILE = 23, + /// Too many open files + EMFILE = 24, + /// Inappropriate ioctl for device + ENOTTY = 25, + /// Text file busy + ETXTBSY = 26, + /// File too large + EFBIG = 27, + /// No space left on device + ENOSPC = 28, + /// Illegal seek + ESPIPE = 29, + /// Read-only file system + EROFS = 30, + /// Too many links + EMLINK = 31, + /// Broken pipe + EPIPE = 32, + /// Numerical argument out of domain + EDOM = 33, + /// Result too large + ERANGE = 34, + + /// Resource temporarily unavailable + EAGAIN = 35, + /// Operation would block + EWOULDBLOCK = EAGAIN, + /// Operation now in progress + EINPROGRESS = 36, + /// Operation already in progress + EALREADY = 37, + + /// Socket operation on non-socket + ENOTSOCK = 38, + /// Destination address required + EDESTADDRREQ = 39, + /// Message too long + EMSGSIZE = 40, + /// Protocol wrong type for socket + EPROTOTYPE = 41, + /// Protocol not available + ENOPROTOOPT = 42, + /// Protocol not supported + EPROTONOSUPPORT = 43, + /// Socket type not supported + ESOCKTNOSUPPORT = 44, + /// Operation not supported + ENOTSUP = 45, + /// Operation not supported on socket + EOPNOTSUPP = ENOTSUP, + /// Protocol family not supported + EPFNOSUPPORT = 46, + /// Address family not supported by protocol family + EAFNOSUPPORT = 47, + /// Address already in use + EADDRINUSE = 48, + /// Can't assign requested address + EADDRNOTAVAIL = 49, + + /// Network is down + ENETDOWN = 50, + /// Network is unreachable + ENETUNREACH = 51, + /// Network dropped connection on reset + ENETRESET = 52, + /// Software caused connection abort + ECONNABORTED = 53, + /// Connection reset by peer + ECONNRESET = 54, + /// No buffer space available + ENOBUFS = 55, + /// Socket is already connected + EISCONN = 56, + /// Socket is not connected + ENOTCONN = 57, + /// Can't send after socket shutdown + ESHUTDOWN = 58, + /// Too many references: can't splice + ETOOMANYREFS = 59, + /// Operation timed out + ETIMEDOUT = 60, + /// Connection refused + ECONNREFUSED = 61, + + /// Too many levels of symbolic links + ELOOP = 62, + /// File name too long + ENAMETOOLONG = 63, + + /// Host is down + EHOSTDOWN = 64, + /// No route to host + EHOSTUNREACH = 65, + /// Directory not empty + ENOTEMPTY = 66, + + /// Too many processes + EPROCLIM = 67, + /// Too many users + EUSERS = 68, + /// Disc quota exceeded + EDQUOT = 69, + + /// Stale NFS file handle + ESTALE = 70, + /// Too many levels of remote in path + EREMOTE = 71, + /// RPC struct is bad + EBADRPC = 72, + /// RPC version wrong + ERPCMISMATCH = 73, + /// RPC prog. not avail + EPROGUNAVAIL = 74, + /// Program version wrong + EPROGMISMATCH = 75, + /// Bad procedure for program + EPROCUNAVAIL = 76, + + /// No locks available + ENOLCK = 77, + /// Function not implemented + ENOSYS = 78, + + /// Inappropriate file type or format + EFTYPE = 79, + /// Authentication error + EAUTH = 80, + /// Need authenticator + ENEEDAUTH = 81, + + /// Device power is off + EPWROFF = 82, + /// Device error, e.g. paper out + EDEVERR = 83, + + /// Value too large to be stored in data type + EOVERFLOW = 84, + + /// Bad executable + EBADEXEC = 85, + /// Bad CPU type in executable + EBADARCH = 86, + /// Shared library version mismatch + ESHLIBVERS = 87, + /// Malformed Macho file + EBADMACHO = 88, + + /// Operation canceled + ECANCELED = 89, + + /// Identifier removed + EIDRM = 90, + /// No message of desired type + ENOMSG = 91, + /// Illegal byte sequence + EILSEQ = 92, + /// Attribute not found + ENOATTR = 93, + + /// Bad message + EBADMSG = 94, + /// Reserved + EMULTIHOP = 95, + /// No message available on STREAM + ENODATA = 96, + /// Reserved + ENOLINK = 97, + /// No STREAM resources + ENOSR = 98, + /// Not a STREAM + ENOSTR = 99, + /// Protocol error + EPROTO = 100, + /// STREAM ioctl timeout + ETIME = 101, + + /// Operation not supported on socket + EOPNOTSUPPSKT = 102, + + /// No such policy registered + ENOPOLICY = 103, + + /// State not recoverable + ENOTRECOVERABLE = 104, + /// Previous owner died + EOWNERDEAD = 105, + /// Interface output queue is full + EQFULL = 106, + } + } +} + diff --git a/Claunia.IO/Interop/Apple/Interop.Apple.stat.cs b/Claunia.IO/Interop/Apple/Interop.Apple.stat.cs index 466816b..fa0199b 100644 --- a/Claunia.IO/Interop/Apple/Interop.Apple.stat.cs +++ b/Claunia.IO/Interop/Apple/Interop.Apple.stat.cs @@ -31,9 +31,10 @@ internal static partial class Interop internal static partial class Apple { /// - /// stat(2) structure + /// stat(2) structure when __DARWIN_64_BIT_INO_T is defined /// - internal struct Stat + [StructLayout(LayoutKind.Sequential)] + internal struct Stat64 { /// /// ID of device containing file @@ -111,6 +112,112 @@ internal static partial class Interop /// [MarshalAs(UnmanagedType.U4)] public uint st_gen; + /// + /// Reserved: DO NOT USE + /// + [MarshalAs(UnmanagedType.U4)] + [Obsolete("RESERVED: DO NOT USE")] + public uint st_lspare; + /// + /// Reserved: DO NOT USE + /// + [MarshalAs(UnmanagedType.ByValArray, + ArraySubType = UnmanagedType.U8, SizeConst = 2)] + [Obsolete("RESERVED: DO NOT USE")] + public ulong[] st_qspare; + } + + /// + /// stat(2) structure when __DARWIN_64_BIT_INO_T is NOT defined + /// + [StructLayout(LayoutKind.Sequential)] + internal struct Stat + { + /// + /// ID of device containing file + /// + [MarshalAs(UnmanagedType.I4)] + public int st_dev; + /// + /// File serial number + /// + [MarshalAs(UnmanagedType.U4)] + public uint st_ino; + /// + /// Mode of file + /// + [MarshalAs(UnmanagedType.U2)] + public mode_t st_mode; + /// + /// Number of hard links + /// + [MarshalAs(UnmanagedType.U2)] + public ushort st_nlink; + /// + /// User ID of the file + /// + [MarshalAs(UnmanagedType.U4)] + public uint st_uid; + /// + /// Group ID of the file + /// + [MarshalAs(UnmanagedType.U4)] + public uint st_gid; + /// + /// Device ID + /// + [MarshalAs(UnmanagedType.I4)] + public int st_rdev; + /// + /// time of last access + /// + public Timespec st_atimespec; + /// + /// time of last data modification + /// + public Timespec st_mtimespec; + /// + /// time of last status change + /// + public Timespec st_ctimespec; + /// + /// file size, in bytes + /// + [MarshalAs(UnmanagedType.I8)] + public long st_size; + /// + /// blocks allocated for file + /// + [MarshalAs(UnmanagedType.I8)] + public long st_blocks; + /// + /// optimal blocksize for I/O + /// + [MarshalAs(UnmanagedType.I4)] + public int st_blksize; + /// + /// user defined flags for file + /// + [MarshalAs(UnmanagedType.U4)] + public flags_t st_flags; + /// + /// file generation number + /// + [MarshalAs(UnmanagedType.U4)] + public uint st_gen; + /// + /// Reserved: DO NOT USE + /// + [MarshalAs(UnmanagedType.U4)] + [Obsolete("RESERVED: DO NOT USE")] + public uint st_lspare; + /// + /// Reserved: DO NOT USE + /// + [MarshalAs(UnmanagedType.ByValArray, + ArraySubType = UnmanagedType.U8, SizeConst = 2)] + [Obsolete("RESERVED: DO NOT USE")] + public ulong[] st_qspare; } /// @@ -326,7 +433,10 @@ internal static partial class Interop } [DllImport(Libraries.Libc, SetLastError = true)] - static extern int stat(string path, out Stat buf); + public static extern int stat(string path, out Stat buf); + + [DllImport(Libraries.Libc, SetLastError = true)] + public static extern int stat64(string path, out Stat64 buf); } } diff --git a/Claunia.IO/Interop/Apple/Interop.Apple.types.cs b/Claunia.IO/Interop/Apple/Interop.Apple.types.cs index e936034..6a6570f 100644 --- a/Claunia.IO/Interop/Apple/Interop.Apple.types.cs +++ b/Claunia.IO/Interop/Apple/Interop.Apple.types.cs @@ -29,7 +29,7 @@ internal static partial class Interop { internal static partial class Apple { - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] internal struct Timespec { // TODO: Mono is 32-bit only on Mac OS X, but when it becomes 64-bit this will become int64 diff --git a/Claunia.IO/Tests/Interop.Apple.cs b/Claunia.IO/Tests/Interop.Apple.cs new file mode 100644 index 0000000..f705841 --- /dev/null +++ b/Claunia.IO/Tests/Interop.Apple.cs @@ -0,0 +1,125 @@ +// +// Interop.Apple.cs +// +// Author: +// Natalia Portillo +// +// Copyright (c) 2015 © Claunia.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +#if DEBUG +using System; +using System.Runtime.InteropServices; +using NUnit.Framework; + +namespace Tests +{ + [TestFixture] + public class InteropApple + { + [Test] + public void TestStat() + { + // Take care, Mono returns UNIX for Mac OS X + if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix) + { + string testFile = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "/.localized"; + + bool testFileExists = System.IO.File.Exists(testFile); + + Assert.AreEqual(true, testFileExists); + + Interop.Apple.Stat stat; + int errno = Interop.Apple.stat(testFile, out stat); + + Assert.AreEqual(0, errno, "stat returned error {0}", (Interop.Apple.Errors)Marshal.GetLastWin32Error()); + Assert.AreEqual(16777219, stat.st_dev); + Assert.AreEqual((Interop.Apple.mode_t)33279, stat.st_mode); + Assert.AreEqual(1, stat.st_nlink); + Assert.AreEqual(2986697, stat.st_ino); + Assert.AreEqual(502, stat.st_uid); + Assert.AreEqual(20, stat.st_gid); + Assert.AreEqual(0, stat.st_rdev); + + Assert.AreEqual(1423425231, stat.st_atimespec.tv_sec); + Assert.AreEqual(0, stat.st_atimespec.tv_nsec); + Assert.AreEqual(1253706844, stat.st_mtimespec.tv_sec); + Assert.AreEqual(0, stat.st_mtimespec.tv_nsec); + Assert.AreEqual(1318498077, stat.st_ctimespec.tv_sec); + Assert.AreEqual(0, stat.st_ctimespec.tv_nsec); + Assert.AreEqual(0, stat.st_size); + Assert.AreEqual(8, stat.st_blocks); + Assert.AreEqual(4096, stat.st_blksize); + Assert.AreEqual((Interop.Apple.flags_t)32768, stat.st_flags); + Assert.AreEqual(0, stat.st_gen); +#pragma warning disable 618 + Assert.AreEqual(0, stat.st_lspare); + Assert.AreEqual(0, stat.st_qspare[0]); + Assert.AreEqual(0, stat.st_qspare[1]); +#pragma warning restore 618 + } + } + + [Test] + public void TestStat64() + { + // Take care, Mono returns UNIX for Mac OS X + if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix) + { + string testFile = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "/.localized"; + + bool testFileExists = System.IO.File.Exists(testFile); + + Assert.AreEqual(true, testFileExists); + + Interop.Apple.Stat64 stat; + int errno = Interop.Apple.stat64(testFile, out stat); + + Assert.AreEqual(0, errno, "stat64 returned error {0}", (Interop.Apple.Errors)Marshal.GetLastWin32Error()); + Assert.AreEqual(16777219, stat.st_dev); + Assert.AreEqual((Interop.Apple.mode_t)33279, stat.st_mode); + Assert.AreEqual(1, stat.st_nlink); + Assert.AreEqual(2986697, stat.st_ino); + Assert.AreEqual(502, stat.st_uid); + Assert.AreEqual(20, stat.st_gid); + Assert.AreEqual(0, stat.st_rdev); + + Assert.AreEqual(1423425231, stat.st_atimespec.tv_sec); + Assert.AreEqual(0, stat.st_atimespec.tv_nsec); + Assert.AreEqual(1253706844, stat.st_mtimespec.tv_sec); + Assert.AreEqual(0, stat.st_mtimespec.tv_nsec); + Assert.AreEqual(1318498077, stat.st_ctimespec.tv_sec); + Assert.AreEqual(0, stat.st_ctimespec.tv_nsec); + Assert.AreEqual(1185301066, stat.st_birthtimespec.tv_sec); + Assert.AreEqual(0, stat.st_birthtimespec.tv_nsec); + Assert.AreEqual(0, stat.st_size); + Assert.AreEqual(8, stat.st_blocks); + Assert.AreEqual(4096, stat.st_blksize); + Assert.AreEqual((Interop.Apple.flags_t)32768, stat.st_flags); + Assert.AreEqual(0, stat.st_gen); +#pragma warning disable 618 + Assert.AreEqual(0, stat.st_lspare); + Assert.AreEqual(0, stat.st_qspare[0]); + Assert.AreEqual(0, stat.st_qspare[1]); +#pragma warning restore 618 + } + } + } +} +#endif diff --git a/Claunia.IO/packages.config b/Claunia.IO/packages.config new file mode 100644 index 0000000..09300da --- /dev/null +++ b/Claunia.IO/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file