diff --git a/library/unistd_readlink.c b/library/unistd_readlink.c index 7a5eeab..2279fa3 100644 --- a/library/unistd_readlink.c +++ b/library/unistd_readlink.c @@ -58,9 +58,19 @@ readlink(const char * path_name, char * buffer, int buffer_size) struct name_translation_info path_name_nti; struct name_translation_info buffer_nti; #endif /* UNIX_PATH_SEMANTICS */ + + D_S(struct bcpl_name, bname); + const size_t name_size = sizeof(bname->name); + BPTR lock = ZERO; int result = ERROR; - int target_length = -1; + struct DevProc *dvp = NULL; + char * new_name = NULL; + char * path_part; + size_t name_len; + size_t new_name_size; + LONG readlink_result; + LONG error; ENTER(); @@ -70,12 +80,12 @@ readlink(const char * path_name, char * buffer, int buffer_size) assert( path_name != NULL && buffer != NULL ); - if(__check_abort_enabled) + if (__check_abort_enabled) __check_abort(); #if defined(CHECK_FOR_NULL_POINTERS) { - if(path_name == NULL || buffer == NULL) + if (path_name == NULL || buffer == NULL) { SHOWSTRING("invalid parameters"); @@ -87,9 +97,9 @@ readlink(const char * path_name, char * buffer, int buffer_size) #if defined(UNIX_PATH_SEMANTICS) { - if(__unix_path_semantics) + if (__unix_path_semantics) { - if(path_name[0] == '\0') + if (path_name[0] == '\0') { SHOWMSG("no name given"); @@ -97,53 +107,144 @@ readlink(const char * path_name, char * buffer, int buffer_size) goto out; } - if(__translate_unix_to_amiga_path_name(&path_name,&path_name_nti) != 0) + if (__translate_unix_to_amiga_path_name(&path_name, &path_name_nti) != 0) goto out; } } #endif /* UNIX_PATH_SEMANTICS */ - D(("trying to get a lock on '%s'",path_name)); + name_len = strlen(path_name); + if (name_len >= name_size) + { + SHOWMSG("name too long"); + + SetIoErr(ERROR_LINE_TOO_LONG); + goto out; + } + + /* Convert the name into a BCPL string. */ + bname->name[0] = name_len; + memcpy(&bname->name[1], path_name, name_len); + + /* Get a handle on the device, volume or assignment name in the path. */ + dvp = GetDeviceProc((STRPTR)path_name, dvp); + if (dvp == NULL) + { + SHOWMSG("dvp == NULL"); + goto out; + } + + /* Try to obtain a lock on the object. It should be a link and not + * a file or directory. + */ + lock = DoPkt(dvp->dvp_Port, ACTION_LOCATE_OBJECT, dvp->dvp_Lock, MKBADDR(bname), SHARED_LOCK, 0, 0); + if (lock != ZERO) + { + SHOWMSG("lock != ZERO"); + + SetIoErr(ERROR_OBJECT_WRONG_TYPE); + goto out; + } + + error = IoErr(); + + if (error != ERROR_IS_SOFT_LINK) + { + SHOWMSG("not a soft link"); + goto out; + } + + /* We only need the leading path name. */ + name_len = ((BYTE *)PathPart(path_name)) - (BYTE *)path_name; + + path_part = malloc(name_len+1); + if (path_part == NULL) + { + SHOWMSG("path_part too long"); + + SetIoErr(ERROR_NO_FREE_STORE); + goto out; + } + + memcpy(path_part, path_name, name_len); + path_part[name_len] = '\0'; PROFILE_OFF(); - lock = __lock((STRPTR)path_name,SHARED_LOCK,&target_length,buffer,(size_t)buffer_size); + lock = Lock((STRPTR)path_part, SHARED_LOCK); PROFILE_ON(); - if(lock != ZERO) + new_name_size = name_size+1; + + /* Provide as much buffer space as possible. */ + if (buffer_size > 0 && (size_t)buffer_size > new_name_size) + new_name_size = buffer_size; + + /* For soft link resolution we need a temporary buffer to + let the file system store the resolved path name in. */ + new_name = malloc(new_name_size); + if (new_name == NULL) { - __set_errno(EINVAL); - goto out; - } - else if (target_length <= 0) /* No a soft-link. */ - { - __set_errno(__translate_io_error_to_errno(IoErr())); + SHOWMSG("new_name too long"); + + SetIoErr(ERROR_NO_FREE_STORE); goto out; } - #if defined(UNIX_PATH_SEMANTICS) + /* Now ask the file system to resolve the entire path. */ + readlink_result = ReadLink(dvp->dvp_Port, lock, (STRPTR)FilePart(path_name), (STRPTR)new_name, (LONG)new_name_size); + if (readlink_result < 0) { - if(__unix_path_semantics) + SHOWMSG("ReadLink"); + + /* This will return either -1 (resolution error) or -2 + (buffer too small). We regard both as trouble. */ + SetIoErr(ERROR_INVALID_COMPONENT_NAME); + goto out; + } + + assert( result > 0 ); + + /* If the caller supplied a buffer, copy as much of the name + as possible into it. */ + if (buffer != NULL && buffer_size > 0) + { + strlcpy(buffer, new_name, buffer_size); + + #if defined(UNIX_PATH_SEMANTICS) { - if(__translate_amiga_to_unix_path_name((char const **)&buffer,&buffer_nti) != 0) - goto out; + if (__unix_path_semantics) + { + if (__translate_amiga_to_unix_path_name((char const **)&buffer, &buffer_nti) != 0) + goto out; - __restore_path_name((char const **)&buffer,&buffer_nti); + __restore_path_name((char const **)&buffer,&buffer_nti); - strcpy(buffer,buffer_nti.substitute); + strcpy(buffer, buffer_nti.substitute); + } } + #endif /* UNIX_PATH_SEMANTICS */ + + result = strlen(buffer); + + SHOWSTRING(buffer); + } + else + { + result = 0; } - #endif /* UNIX_PATH_SEMANTICS */ - - result = strlen(buffer); - - SHOWSTRING(buffer); out: PROFILE_OFF(); + + if (dvp != NULL) + FreeDeviceProc(dvp); + UnLock(lock); + PROFILE_ON(); RETURN(result); - return(result); + + return result; }