Kaynağa Gözat

LibCore: Add File::read_link() :^)

This is a convenient wrapper around readlink() that hides away the details
of buffers and buffer sizes, and simply returns a String. The best part is it
doesn't rely on PATH_MAX :D

It comes in two versions, for Serenity, where we can pass non-null-terminated
strings to syscalls, and where sys$readlink() returns the total link size, and
for other systems, where we have to copy out the string, and always have to do
two syscalls.
Sergey Bugaev 5 yıl önce
ebeveyn
işleme
d89843f96f
2 değiştirilmiş dosya ile 74 ekleme ve 0 silme
  1. 73 0
      Libraries/LibCore/File.cpp
  2. 1 0
      Libraries/LibCore/File.h

+ 73 - 0
Libraries/LibCore/File.cpp

@@ -24,6 +24,9 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifdef __serenity__
+#    include <Kernel/Syscall.h>
+#endif
 #include <LibCore/File.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -129,5 +132,75 @@ String File::real_path_for(const String& filename)
     return real_path;
 }
 
+#ifdef __serenity__
+
+String File::read_link(const StringView& link_path)
+{
+    // First, try using a 64-byte buffer, that ought to be enough for anybody.
+    char small_buffer[64];
+    Syscall::SC_readlink_params small_params {
+        { link_path.characters_without_null_termination(), link_path.length() },
+        { small_buffer, sizeof(small_buffer) }
+    };
+    int rc = syscall(SC_readlink, &small_params);
+    if (rc < 0) {
+        errno = -rc;
+        return {};
+    }
+    size_t size = rc;
+    // If the call was successful, the syscall (unlike the LibC wrapper)
+    // returns the full size of the link. Let's see if our small buffer
+    // was enough to read the whole link.
+    if (size <= sizeof(small_buffer))
+        return { small_buffer, size };
+    // Nope, but at least now we know the right size.
+    char* large_buffer_ptr;
+    auto large_buffer = StringImpl::create_uninitialized(size, large_buffer_ptr);
+    Syscall::SC_readlink_params large_params {
+        { link_path.characters_without_null_termination(), link_path.length() },
+        { large_buffer_ptr, (size_t)size }
+    };
+    rc = syscall(SC_readlink, &large_params);
+    if (rc < 0) {
+        errno = -rc;
+        return {};
+    }
+    size_t new_size = rc;
+    if (new_size == size)
+        return { *large_buffer };
+
+    // If we're here, the symlink has changed while we were looking at it.
+    // If it became shorter, our buffer is valid, we just have to trim it a bit.
+    if (new_size < size)
+        return { large_buffer_ptr, new_size };
+    // Otherwise, here's not much we can do, unless we want to loop endlessly
+    // in this case. Let's leave it up to the caller whether to loop.
+    errno = -EAGAIN;
+    return {};
+}
+
+#else
+
+// This is a sad version for other systems. It has to always make a copy of the
+// link path, and to always make two syscalls to get the right size first.
+String File::read_link(const StringView& link_path)
+{
+    String link_path_str = link_path;
+    struct stat statbuf;
+    int rc = lstat(link_path_str.characters(), &statbuf);
+    if (rc < 0)
+        return {};
+    char* buffer_ptr;
+    auto buffer = StringImpl::create_uninitialized(statbuf.st_size, buffer_ptr);
+    rc = readlink(link_path_str.characters(), buffer_ptr, statbuf.st_size);
+    if (rc < 0)
+        return {};
+    // (See above.)
+    if (rc == statbuf.st_size)
+        return { *buffer };
+    return { buffer_ptr, (size_t)rc };
+}
+
+#endif
 
 }

+ 1 - 0
Libraries/LibCore/File.h

@@ -47,6 +47,7 @@ public:
 
     static bool exists(const String& filename);
     static String real_path_for(const String& filename);
+    static String read_link(const StringView& link_path);
 
     virtual bool open(IODevice::OpenMode) override;