Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OS.FileSys.fullPath gives EINVAL exception on Solaris 10 #171

Closed
binghe opened this issue Feb 22, 2022 · 5 comments
Closed

OS.FileSys.fullPath gives EINVAL exception on Solaris 10 #171

binghe opened this issue Feb 22, 2022 · 5 comments

Comments

@binghe
Copy link

binghe commented Feb 22, 2022

Hi,

On Solaris 10 (x86), the function OS.FileSys.fullPath strangely gives EINVAL exception on any path string, e.g.:

$ poly
Poly/ML 5.9 Release (Git version v5.9-1-g39d96a2)
> OS.FileSys.fullPath "/";
Exception- SysErr ("Invalid argument", SOME EINVAL) raised

This issue only happens on Solaris 10. (It works fine on Solaris 11.) PolyML 5.8.1 built on Solaris 10 also has the same issue:

$ poly
Poly/ML 5.8.1 Release (Git version v5.8.1)
> OS.FileSys.fullPath "/";
Exception- SysErr ("Invalid argument", SOME EINVAL) raised

Regards,

Chun Tian

@binghe
Copy link
Author

binghe commented Feb 22, 2022

Hmmm, I found the root causes. There's a call of realpath(cFileName, NULL) in fullPath (libpolyml/basicio.cpp):

Handle fullPath(TaskData *taskData, Handle filename)
{
    TempString cFileName;

    /* Special case of an empty string. */
    if (PolyStringLength(filename->Word()) == 0) cFileName = strdup(".");
    else cFileName = Poly_string_to_C_alloc(filename->Word());
    if (cFileName == 0) raise_syscall(taskData, "Insufficient memory", NOMEMORY);
    TempCString resBuf(realpath(cFileName, NULL));
    if (resBuf == NULL)
        raise_syscall(taskData, "realpath failed", ERRORNUMBER);
    /* Some versions of Unix don't check the final component
        of a file.  To be consistent try doing a "stat" of
        the resulting string to check it exists. */
    struct stat fbuff;
    if (stat(resBuf, &fbuff) != 0)
        raise_syscall(taskData, "stat failed", ERRORNUMBER);
    return(SAVE(C_string_to_Poly(taskData, resBuf)));
}

On Solaris 10, the man page (3C) of realpath() says the following

Standard C Library Functions                         realpath(3C)

NAME
     realpath - resolve pathname

SYNOPSIS
     #include <stdlib.h>

     char *realpath(const char *restrict  file_name,  char  *res-
     trict resolved_name);

DESCRIPTION
     The realpath() function derives, from the  pathname  pointed
     to  by  file_name,  an absolute pathname that names the same
     file, whose resolution does not involve ".", "..",  or  sym-
     bolic  links.  The  generated  pathname is stored as a null-
     terminated string, up  to  a  maximum  of  {PATH_MAX}  bytes
     (defined  in  limits.h(3HEAD)),  in the buffer pointed to by
     resolved_name.

RETURN VALUES
     On successful completion, realpath() returns  a  pointer  to
     the  resolved  name.   Otherwise,  realpath() returns a null
     pointer and sets errno to indicate the error, and  the  con-
     tents  of the buffer pointed to by resolved_name are left in
     an indeterminate state.

...

ERRORS
     The realpath() function will fail if:

     EACCES          Read or search permission was denied  for  a
                     component of file_name.

     EINVAL          Either the file_name or resolved_name  argu-
                     ment is a null pointer.

Because the 2nd parameter is NULL, realpath returns EINVAL on Solaris 10. I also checked man page of Solaris 11, which only returns EINVAL if the first argument is NULL:

Standard C Library Functions                                      realpath(3C)

NAME
       realpath, frealpath, canonicalize_file_name - resolve pathname

SYNOPSIS
       #include <stdlib.h>

       char *realpath(const char *restrict file_name,
            char *restrict resolved_name);

DESCRIPTION
       The  realpath()  function  derives,  from  the  pathname  pointed to by
       file_name, an absolute pathname that resolves  to  the  same  directory
       entry,  whose resolution does not involve ".", "..", or symbolic links.
       If resolved_name is not null, the generated pathname  is  stored  as  a
       null-terminated  string, up to a maximum of {PATH_MAX} (defined in lim-
       its.h(3HEAD)) bytes in the  buffer  pointed  to  by  resolved_name.  If
       resolved_name  is null, the generated pathname is stored as a null-ter-
       minated string in a buffer that is  allocated  as  if  malloc(3C)  were
       called.

...
ERRORS
       The realpath() function will fail if:

       EACCES          Search permission was denied for  a  component  of  the
                       path prefix of file_name.

       EINVAL          The file_name argument is a null pointer.

Could you please make some changes to support Solaris 10. For example, instead of using the return value of realpath, now we first allocate null-terminated string, up to a maximum of {PATH_MAX}, then use it as the 2nd argument of realpath?

Regards,

Chun Tian

@binghe
Copy link
Author

binghe commented Feb 22, 2022

According to Linux man pages [1], glibc versions before 2.3 behaves the same as Solaris 10:

       EINVAL path is NULL.  (In glibc versions before 2.3, this error
              is also returned if resolved_path is NULL.)

Thus my above suggested changes may also make PolyML work better on old Linux systems.

[1] https://man7.org/linux/man-pages/man3/realpath.3.html

@binghe
Copy link
Author

binghe commented Feb 22, 2022

The following patch works for me (FILENAME_MAX is a constant defined in C standard [1] but please also see this [2])

diff --git a/libpolyml/basicio.cpp b/libpolyml/basicio.cpp
index 0457524c..77d517ff 100644
--- a/libpolyml/basicio.cpp
+++ b/libpolyml/basicio.cpp
@@ -652,8 +652,10 @@ Handle fullPath(TaskData *taskData, Handle filename)
     if (PolyStringLength(filename->Word()) == 0) cFileName = strdup(".");
     else cFileName = Poly_string_to_C_alloc(filename->Word());
     if (cFileName == 0) raise_syscall(taskData, "Insufficient memory", NOMEMORY);
-    TempCString resBuf(realpath(cFileName, NULL));
-    if (resBuf == NULL)
+
+    /* old UNIX systems require the 2nd argument of realpath() be non-NULL */
+    TempCString resBuf((char *)malloc(FILENAME_MAX * sizeof(char)));
+    if (NULL == realpath(cFileName, resBuf))
         raise_syscall(taskData, "realpath failed", ERRORNUMBER);
     /* Some versions of Unix don't check the final component
         of a file.  To be consistent try doing a "stat" of

[1] https://www.cplusplus.com/reference/cstdio/FILENAME_MAX/
[2] https://stackoverflow.com/questions/65174086/is-there-any-difference-between-filename-max-and-path-max-in-c

@dcjm
Copy link
Contributor

dcjm commented Feb 24, 2022

I've added some code to handle this along with a regression test. They are currently in the SolarisRealpath branch. Could you please test this and let me know if it solves it for you. I found an old Solaris 10 installation disc and attempted to install Poly/ML in a virtual machine but ran into so many problems that I have given up.

@binghe
Copy link
Author

binghe commented Feb 24, 2022

I've added some code to handle this along with a regression test. They are currently in the SolarisRealpath branch. Could you please test this and let me know if it solves it for you. I found an old Solaris 10 installation disc and attempted to install Poly/ML in a virtual machine but ran into so many problems that I have given up.

Thank you for your time! I just built PolyML in this branch and make a quick test: it works well on Solaris 10:

Poly/ML 5.9.1 Testing (Git version v5.9-182-g0ef5380)
> OS.FileSys.fullPath ".";
val it = "/export/home/binghe": string

P. S. the latest Solaris 10 installation disc can be download at https://www.oracle.com/solaris/solaris10/downloads/solaris10-get-jsp-downloads.html but to build PolyML you need to install GCC 5.5.0 from https://www.opencsw.org and put /opt/csw/bin, etc. into $PATH. I believe GNU make (gmake) is also required.

If you are still interested in having a Solaris x86 VM, please tell me your hypervisor software (VMware, VirtualBox, etc.) and I can prepare an out-of-box Solaris VM for you!

--Chun

@dcjm dcjm closed this as completed in 969fa4a Feb 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants