}
public string GetWorkingDirectory () {
- int len = 256;
-
- while (true) {
- StringBuilder sb = new StringBuilder (len);
-
- int res = Syscall.readlink ("/proc/" + pid + "/cwd", sb);
- if (res == -1)
- throw new IOException ("Syscall.readlink () failed with error " + res + ".");
- else if (res == len) {
- len = len * 2;
- } else {
- return sb.ToString ();
- }
- }
+ return UnixPath.ReadLink ("/proc/" + pid + "/cwd");
}
/*
+Mono.Unix/ReadlinkTest.cs
Mono.Unix/StdioFileStreamTest.cs
Mono.Unix/UnixEncodingTest.cs
Mono.Unix/UnixGroupTest.cs
[MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))]
string newpath);
+ delegate long DoReadlinkFun (byte[] target);
+
+ // Helper function for readlink(string, StringBuilder) and readlinkat (int, string, StringBuilder)
+ static int ReadlinkIntoStringBuilder (DoReadlinkFun doReadlink, [Out] StringBuilder buf, ulong bufsiz)
+ {
+ // bufsiz > int.MaxValue can't work because StringBuilder can store only int.MaxValue chars
+ int bufsizInt = checked ((int) bufsiz);
+ var target = new byte [bufsizInt];
+
+ var r = doReadlink (target);
+ if (r < 0)
+ return checked ((int) r);
+
+ buf.Length = 0;
+ var chars = UnixEncoding.Instance.GetChars (target, 0, checked ((int) r));
+ // Make sure that at more bufsiz chars are written
+ buf.Append (chars, 0, System.Math.Min (bufsizInt, chars.Length));
+ if (r == bufsizInt) {
+ // may not have read full contents; fill 'buf' so that caller can properly check
+ buf.Append (new string ('\x00', bufsizInt - buf.Length));
+ }
+ return buf.Length;
+ }
+
// readlink(2)
- // int readlink(const char *path, char *buf, size_t bufsize);
+ // ssize_t readlink(const char *path, char *buf, size_t bufsize);
+ public static int readlink (string path, [Out] StringBuilder buf, ulong bufsiz)
+ {
+ return ReadlinkIntoStringBuilder (target => readlink (path, target), buf, bufsiz);
+ }
+
+ public static int readlink (string path, [Out] StringBuilder buf)
+ {
+ return readlink (path, buf, (ulong) buf.Capacity);
+ }
+
[DllImport (MPH, SetLastError=true,
EntryPoint="Mono_Posix_Syscall_readlink")]
- public static extern int readlink (
+ private static extern long readlink (
[MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))]
- string path, [Out] StringBuilder buf, ulong bufsiz);
+ string path, byte[] buf, ulong bufsiz);
- public static int readlink (string path, [Out] StringBuilder buf)
+ public static long readlink (string path, byte[] buf)
{
- return readlink (path, buf, (ulong) buf.Capacity);
+ return readlink (path, buf, (ulong) buf.LongLength);
}
[DllImport (LIBC, SetLastError=true)]
}
// readlinkat(2)
- // int readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsize);
+ // ssize_t readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsize);
+ public static int readlinkat (int dirfd, string pathname, [Out] StringBuilder buf, ulong bufsiz)
+ {
+ return ReadlinkIntoStringBuilder (target => readlinkat (dirfd, pathname, target), buf, bufsiz);
+ }
+
+ public static int readlinkat (int dirfd, string pathname, [Out] StringBuilder buf)
+ {
+ return readlinkat (dirfd, pathname, buf, (ulong) buf.Capacity);
+ }
+
[DllImport (MPH, SetLastError=true,
EntryPoint="Mono_Posix_Syscall_readlinkat")]
- public static extern int readlinkat (int dirfd,
+ private static extern long readlinkat (int dirfd,
[MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))]
- string pathname, [Out] StringBuilder buf, ulong bufsiz);
+ string pathname, byte[] buf, ulong bufsiz);
- public static int readlinkat (int dirfd, string pathname, [Out] StringBuilder buf)
+ public static long readlinkat (int dirfd, string pathname, byte[] buf)
{
- return readlinkat (dirfd, pathname, buf, (ulong) buf.Capacity);
+ return readlinkat (dirfd, pathname, buf, (ulong) buf.LongLength);
}
[DllImport (LIBC, SetLastError=true)]
// Read the specified symbolic link. If the file isn't a symbolic link,
// return null; otherwise, return the contents of the symbolic link.
- //
- // readlink(2) is horribly evil, as there is no way to query how big the
- // symlink contents are. Consequently, it's trial and error...
internal static string ReadSymbolicLink (string path)
{
- StringBuilder buf = new StringBuilder (256);
+ string target = TryReadLink (path);
+ if (target == null) {
+ Native.Errno errno = Native.Stdlib.GetLastError ();
+ if (errno != Native.Errno.EINVAL)
+ UnixMarshal.ThrowExceptionForError (errno);
+ }
+ return target;
+ }
+
+ public static string TryReadLink (string path)
+ {
+ byte[] buf = new byte[256];
do {
- int r = Native.Syscall.readlink (path, buf);
- if (r < 0) {
- Native.Errno e;
- switch (e = Native.Stdlib.GetLastError()) {
- case Native.Errno.EINVAL:
- // path isn't a symbolic link
- return null;
- default:
- UnixMarshal.ThrowExceptionForError (e);
- break;
- }
- }
- else if (r == buf.Capacity) {
- buf.Capacity *= 2;
- }
+ long r = Native.Syscall.readlink (path, buf);
+ if (r < 0)
+ return null;
+ else if (r == buf.Length)
+ buf = new byte[checked (buf.LongLength * 2)];
else
- return buf.ToString (0, r);
+ return UnixEncoding.Instance.GetString (buf, 0, checked ((int) r));
} while (true);
}
- // Read the specified symbolic link. If the file isn't a symbolic link,
- // return null; otherwise, return the contents of the symbolic link.
- //
- // readlink(2) is horribly evil, as there is no way to query how big the
- // symlink contents are. Consequently, it's trial and error...
- private static string ReadSymbolicLink (string path, out Native.Errno errno)
+ public static string TryReadLinkAt (int dirfd, string path)
{
- errno = (Native.Errno) 0;
- StringBuilder buf = new StringBuilder (256);
+ byte[] buf = new byte[256];
do {
- int r = Native.Syscall.readlink (path, buf);
- if (r < 0) {
- errno = Native.Stdlib.GetLastError ();
+ long r = Native.Syscall.readlinkat (dirfd, path, buf);
+ if (r < 0)
return null;
- }
- else if (r == buf.Capacity) {
- buf.Capacity *= 2;
- }
+ else if (r == buf.Length)
+ buf = new byte[checked (buf.LongLength * 2)];
else
- return buf.ToString (0, r);
+ return UnixEncoding.Instance.GetString (buf, 0, checked ((int) r));
} while (true);
}
- public static string TryReadLink (string path)
+ public static string ReadLink (string path)
{
- Native.Errno errno;
- return ReadSymbolicLink (path, out errno);
+ string target = TryReadLink (path);
+ if (target == null)
+ UnixMarshal.ThrowExceptionForLastError ();
+ return target;
}
- public static string ReadLink (string path)
+ public static string ReadLinkAt (int dirfd, string path)
{
- Native.Errno errno;
- path = ReadSymbolicLink (path, out errno);
- if (errno != 0)
- UnixMarshal.ThrowExceptionForError (errno);
- return path;
+ string target = TryReadLinkAt (dirfd, path);
+ if (target == null)
+ UnixMarshal.ThrowExceptionForLastError ();
+ return target;
}
public static bool IsPathRooted (string path)
public string ContentsPath {
get {
- return ReadLink ();
+ return UnixPath.ReadLink (FullPath);
}
}
public bool HasContents {
get {
- return TryReadLink () != null;
+ return UnixPath.TryReadLink (FullPath) != null;
}
}
public UnixFileSystemInfo GetContents ()
{
- ReadLink ();
return UnixFileSystemInfo.GetFileSystemEntry (
UnixPath.Combine (UnixPath.GetDirectoryName (FullPath),
ContentsPath));
{
return Native.Syscall.lstat (path, out stat) == 0;
}
-
- private string ReadLink ()
- {
- string r = TryReadLink ();
- if (r == null)
- UnixMarshal.ThrowExceptionForLastError ();
- return r;
- }
-
- private string TryReadLink ()
- {
- StringBuilder sb = new StringBuilder ((int) base.Length+1);
- int r = Native.Syscall.readlink (FullPath, sb);
- if (r == -1)
- return null;
- return sb.ToString (0, r);
- }
}
}
--- /dev/null
+//
+// readlink() / readlinkat() Test Cases
+//
+// Authors:
+// Steffen Kiess (s-kiess@web.de)
+//
+// Copyright (C) 2013 Steffen Kiess
+//
+
+using System;
+using System.IO;
+using System.Text;
+
+using Mono.Unix;
+using Mono.Unix.Native;
+
+using NUnit.Framework;
+
+namespace MonoTests.Mono.Unix
+{
+ [TestFixture, Category ("NotDotNet")]
+ public class ReadlinkTest {
+
+ static string[] Targets = {
+ // Simple test cases
+ "a",
+ "test",
+ // With non-ASCII characters
+ "ä",
+ "test ö test",
+ // With non-UTF8 bytes
+ UnixEncoding.Instance.GetString (new byte[] {0xff, 0x80, 0x41, 0x80}),
+ // Size is roughly initial size of buffer
+ new string ('a', 255),
+ new string ('a', 256),
+ new string ('a', 257),
+ // With non-ASCII characters, size is roughly initial size of buffer
+ "ä" + new string ('a', 253), // 254 chars, 255 bytes
+ "ä" + new string ('a', 254), // 255 chars, 256 bytes
+ "ä" + new string ('a', 255), // 256 chars, 257 bytes
+ "ä" + new string ('a', 256), // 257 chars, 258 bytes
+ new string ('a', 253) + "ä", // 254 chars, 255 bytes
+ new string ('a', 254) + "ä", // 255 chars, 256 bytes
+ new string ('a', 255) + "ä", // 256 chars, 257 bytes
+ new string ('a', 256) + "ä", // 257 chars, 258 bytes
+ // With non-UTF8 bytes, size is roughly initial size of buffer
+ "\0\u00ff" + new string ('a', 253), // 255 chars, 254 bytes
+ "\0\u00ff" + new string ('a', 254), // 256 chars, 255 bytes
+ "\0\u00ff" + new string ('a', 255), // 257 chars, 256 bytes
+ "\0\u00ff" + new string ('a', 256), // 258 chars, 257 bytes
+ new string ('a', 253) + "\0\u00ff", // 255 chars, 254 bytes
+ new string ('a', 254) + "\0\u00ff", // 256 chars, 255 bytes
+ new string ('a', 255) + "\0\u00ff", // 257 chars, 256 bytes
+ new string ('a', 256) + "\0\u00ff", // 258 chars, 257 bytes
+ };
+
+ bool HaveReadlinkAt;
+ string TempFolder;
+ int TempFD;
+
+ [SetUp]
+ public void SetUp ()
+ {
+ HaveReadlinkAt = false;
+ try {
+ Syscall.readlinkat (-1, "", new byte[1]);
+ HaveReadlinkAt = true;
+ } catch (EntryPointNotFoundException) {
+ }
+
+
+ TempFolder = Path.Combine (Path.GetTempPath (), this.GetType ().FullName);
+
+ if (Directory.Exists (TempFolder))
+ //Directory.Delete (TempFolder, true); // Fails for long link target paths
+ new UnixDirectoryInfo (TempFolder).Delete (true);
+
+ Directory.CreateDirectory (TempFolder);
+
+ TempFD = Syscall.open (TempFolder, OpenFlags.O_RDONLY | OpenFlags.O_DIRECTORY);
+ if (TempFD < 0)
+ UnixMarshal.ThrowExceptionForLastError ();
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ if (Syscall.close (TempFD) < 0)
+ UnixMarshal.ThrowExceptionForLastError ();
+
+ if (Directory.Exists (TempFolder))
+ //Directory.Delete (TempFolder, true); // Fails for long link target paths
+ new UnixDirectoryInfo (TempFolder).Delete (true);
+ }
+
+ void CreateLink (string s)
+ {
+ string link = UnixPath.Combine (TempFolder, "link");
+
+ //File.Delete (link); // Fails for long link target paths
+ if (Syscall.unlink (link) < 0 && Stdlib.GetLastError () != Errno.ENOENT)
+ UnixMarshal.ThrowExceptionForLastError ();
+
+ if (Syscall.symlink (s, link) < 0)
+ UnixMarshal.ThrowExceptionForLastError ();
+ }
+
+ [Test]
+ public void ReadLink ()
+ {
+ foreach (string s in Targets) {
+ string link = UnixPath.Combine (TempFolder, "link");
+
+ CreateLink (s);
+
+ var target = UnixPath.ReadLink (link);
+ Assert.AreEqual (s, target);
+ }
+ }
+
+ [Test]
+ public void ReadLinkAt ()
+ {
+ if (!HaveReadlinkAt)
+ return;
+
+ foreach (string s in Targets) {
+ CreateLink (s);
+
+ var target = UnixPath.ReadLinkAt (TempFD, "link");
+ Assert.AreEqual (s, target);
+ }
+ }
+
+ [Test]
+ public void TryReadLink ()
+ {
+ foreach (string s in Targets) {
+ string link = UnixPath.Combine (TempFolder, "link");
+
+ CreateLink (s);
+
+ var target = UnixPath.TryReadLink (link);
+ Assert.AreEqual (s, target);
+ }
+ }
+
+ [Test]
+ public void TryReadLinkAt ()
+ {
+ if (!HaveReadlinkAt)
+ return;
+
+ foreach (string s in Targets) {
+ CreateLink (s);
+
+ var target = UnixPath.TryReadLinkAt (TempFD, "link");
+ Assert.AreEqual (s, target);
+ }
+ }
+
+ [Test]
+ public void readlink_byte ()
+ {
+ foreach (string s in Targets) {
+ string link = UnixPath.Combine (TempFolder, "link");
+
+ CreateLink (s);
+
+ string target = null;
+ byte[] buf = new byte[256];
+ do {
+ long r = Syscall.readlink (link, buf);
+ if (r < 0)
+ UnixMarshal.ThrowExceptionForLastError ();
+ Assert.GreaterOrEqual (buf.Length, r);
+ if (r == buf.Length)
+ buf = new byte[checked (buf.Length * 2)];
+ else
+ target = UnixEncoding.Instance.GetString (buf, 0, checked ((int) r));
+ } while (target == null);
+
+ Assert.AreEqual (s, target);
+ }
+ }
+
+ [Test]
+ public void readlinkat_byte ()
+ {
+ if (!HaveReadlinkAt)
+ return;
+
+ foreach (string s in Targets) {
+ CreateLink (s);
+
+ string target = null;
+ byte[] buf = new byte[256];
+ do {
+ long r = Syscall.readlinkat (TempFD, "link", buf);
+ if (r < 0)
+ UnixMarshal.ThrowExceptionForLastError ();
+ Assert.GreaterOrEqual (buf.Length, r);
+ if (r == buf.Length)
+ buf = new byte[checked (buf.Length * 2)];
+ else
+ target = UnixEncoding.Instance.GetString (buf, 0, checked ((int) r));
+ } while (target == null);
+
+ Assert.AreEqual (s, target);
+ }
+ }
+
+ [Test]
+ public void readlink_char ()
+ {
+ foreach (string s in Targets) {
+ string link = UnixPath.Combine (TempFolder, "link");
+
+ CreateLink (s);
+
+ var sb = new StringBuilder (256);
+ do {
+ int oldCapacity = sb.Capacity;
+ int r = Syscall.readlink (link, sb);
+ Assert.AreEqual (oldCapacity, sb.Capacity);
+ if (r < 0)
+ UnixMarshal.ThrowExceptionForLastError ();
+ Assert.AreEqual (r, sb.Length);
+ Assert.GreaterOrEqual (sb.Capacity, r);
+ if (r == sb.Capacity)
+ checked { sb.Capacity *= 2; }
+ else
+ break;
+ } while (true);
+ var target = sb.ToString ();
+
+ Assert.AreEqual (s, target);
+ }
+ }
+
+ [Test]
+ public void readlinkat_char ()
+ {
+ foreach (string s in Targets) {
+ CreateLink (s);
+
+ var sb = new StringBuilder (256);
+ do {
+ int oldCapacity = sb.Capacity;
+ int r = Syscall.readlinkat (TempFD, "link", sb);
+ Assert.AreEqual (oldCapacity, sb.Capacity);
+ if (r < 0)
+ UnixMarshal.ThrowExceptionForLastError ();
+ Assert.AreEqual (r, sb.Length);
+ Assert.GreaterOrEqual (sb.Capacity, r);
+ if (r == sb.Capacity)
+ checked { sb.Capacity *= 2; }
+ else
+ break;
+ } while (true);
+ var target = sb.ToString ();
+
+ Assert.AreEqual (s, target);
+ }
+ }
+
+ [Test]
+ public void ReadlinkMultiByteChar ()
+ {
+ string link = UnixPath.Combine (TempFolder, "link");
+
+ CreateLink ("á");
+
+ var sb = new StringBuilder (2);
+ int res = Syscall.readlink (link, sb);
+ if (res < 0)
+ UnixMarshal.ThrowExceptionForLastError ();
+
+ Assert.AreEqual (res, 2);
+ Assert.AreEqual (sb.Length, 2);
+ Assert.AreEqual (sb.Capacity, 2);
+ Assert.AreEqual (sb.ToString (), "á\u0000");
+ }
+ }
+}
gint64 Mono_Posix_Syscall_read (int fd, void* buf, guint64 count);
int Mono_Posix_Syscall_readdir (void* dir, struct Mono_Posix_Syscall__Dirent* dentry);
int Mono_Posix_Syscall_readdir_r (void* dirp, struct Mono_Posix_Syscall__Dirent* entry, void** result);
-int Mono_Posix_Syscall_readlink (const char* path, char* buf, guint64 bufsiz);
-int Mono_Posix_Syscall_readlinkat (int dirfd, const char* pathname, char* buf, guint64 bufsiz);
+gint64 Mono_Posix_Syscall_readlink (const char* path, unsigned char* buf, guint64 bufsiz);
+gint64 Mono_Posix_Syscall_readlinkat (int dirfd, const char* pathname, unsigned char* buf, guint64 bufsiz);
gint64 Mono_Posix_Syscall_readv (int fd, struct Mono_Posix_Iovec* iov, int iovcnt);
int Mono_Posix_Syscall_remap_file_pages (void* start, guint64 size, int prot, gint64 pgoff, int flags);
int Mono_Posix_Syscall_removexattr (const char* path, const char* name);
}
#endif /* ndef HAVE_TTYNAME_R */
-gint32
-Mono_Posix_Syscall_readlink (const char *path, char *buf, mph_size_t len)
+gint64
+Mono_Posix_Syscall_readlink (const char *path, unsigned char *buf, mph_size_t len)
{
- int r;
+ gint64 r;
mph_return_if_size_t_overflow (len);
- r = readlink (path, buf, (size_t) len);
+ r = readlink (path, (char*) buf, (size_t) len);
if (r >= 0 && r < len)
buf [r] = '\0';
return r;
}
#ifdef HAVE_READLINKAT
-gint32
-Mono_Posix_Syscall_readlinkat (int dirfd, const char *path, char *buf, mph_size_t len)
+gint64
+Mono_Posix_Syscall_readlinkat (int dirfd, const char *path, unsigned char *buf, mph_size_t len)
{
- int r;
+ gint64 r;
mph_return_if_size_t_overflow (len);
- r = readlinkat (dirfd, path, buf, (size_t) len);
+ r = readlinkat (dirfd, path, (char*) buf, (size_t) len);
if (r >= 0 && r < len)
buf [r] = '\0';
return r;