|  | /* Copyright (C) 1992-2016 Free Software Foundation, Inc. | 
|  | This file is part of the GNU C Library. | 
|  |  | 
|  | The GNU C Library is free software; you can redistribute it and/or | 
|  | modify it under the terms of the GNU Lesser General Public | 
|  | License as published by the Free Software Foundation; either | 
|  | version 2.1 of the License, or (at your option) any later version. | 
|  |  | 
|  | The GNU C Library is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | Lesser General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU Lesser General Public | 
|  | License along with the GNU C Library; if not, see | 
|  | <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include <hurd.h> | 
|  | #include <hurd/lookup.h> | 
|  | #include <string.h> | 
|  | #include <fcntl.h> | 
|  |  | 
|  |  | 
|  | /* Translate the error from dir_lookup into the error the user sees.  */ | 
|  | static inline error_t | 
|  | lookup_error (error_t error) | 
|  | { | 
|  | switch (error) | 
|  | { | 
|  | case EOPNOTSUPP: | 
|  | case MIG_BAD_ID: | 
|  | /* These indicate that the server does not understand dir_lookup | 
|  | at all.  If it were a directory, it would, by definition.  */ | 
|  | return ENOTDIR; | 
|  | default: | 
|  | return error; | 
|  | } | 
|  | } | 
|  |  | 
|  | error_t | 
|  | __hurd_file_name_lookup (error_t (*use_init_port) | 
|  | (int which, error_t (*operate) (file_t)), | 
|  | file_t (*get_dtable_port) (int fd), | 
|  | error_t (*lookup) | 
|  | (file_t dir, char *name, int flags, mode_t mode, | 
|  | retry_type *do_retry, string_t retry_name, | 
|  | mach_port_t *result), | 
|  | const char *file_name, int flags, mode_t mode, | 
|  | file_t *result) | 
|  | { | 
|  | error_t err; | 
|  | enum retry_type doretry; | 
|  | char retryname[1024];		/* XXX string_t LOSES! */ | 
|  | int startport; | 
|  |  | 
|  | error_t lookup_op (mach_port_t startdir) | 
|  | { | 
|  | return lookup_error ((*lookup) (startdir, file_name, flags, mode, | 
|  | &doretry, retryname, result)); | 
|  | } | 
|  |  | 
|  | if (! lookup) | 
|  | lookup = __dir_lookup; | 
|  |  | 
|  | if (file_name[0] == '\0') | 
|  | return ENOENT; | 
|  |  | 
|  | startport = (file_name[0] == '/') ? INIT_PORT_CRDIR : INIT_PORT_CWDIR; | 
|  | while (file_name[0] == '/') | 
|  | file_name++; | 
|  |  | 
|  | if (flags & O_NOFOLLOW)	/* See lookup-retry.c about O_NOFOLLOW.  */ | 
|  | flags |= O_NOTRANS; | 
|  |  | 
|  | if (flags & O_DIRECTORY) | 
|  | { | 
|  | /* The caller wants to require that the file we look up is a directory. | 
|  | We can do this without an extra RPC by appending a trailing slash | 
|  | to the file name we look up.  */ | 
|  | size_t len = strlen (file_name); | 
|  | if (len == 0) | 
|  | file_name = "/"; | 
|  | else if (file_name[len - 1] != '/') | 
|  | { | 
|  | char *n = alloca (len + 2); | 
|  | memcpy (n, file_name, len); | 
|  | n[len] = '/'; | 
|  | n[len + 1] = '\0'; | 
|  | file_name = n; | 
|  | } | 
|  | } | 
|  |  | 
|  | err = (*use_init_port) (startport, &lookup_op); | 
|  | if (! err) | 
|  | err = __hurd_file_name_lookup_retry (use_init_port, get_dtable_port, | 
|  | lookup, doretry, retryname, | 
|  | flags, mode, result); | 
|  |  | 
|  | return err; | 
|  | } | 
|  | weak_alias (__hurd_file_name_lookup, hurd_file_name_lookup) | 
|  |  | 
|  | error_t | 
|  | __hurd_file_name_split (error_t (*use_init_port) | 
|  | (int which, error_t (*operate) (file_t)), | 
|  | file_t (*get_dtable_port) (int fd), | 
|  | error_t (*lookup) | 
|  | (file_t dir, char *name, int flags, mode_t mode, | 
|  | retry_type *do_retry, string_t retry_name, | 
|  | mach_port_t *result), | 
|  | const char *file_name, | 
|  | file_t *dir, char **name) | 
|  | { | 
|  | error_t addref (file_t crdir) | 
|  | { | 
|  | *dir = crdir; | 
|  | return __mach_port_mod_refs (__mach_task_self (), | 
|  | crdir, MACH_PORT_RIGHT_SEND, +1); | 
|  | } | 
|  |  | 
|  | const char *lastslash = strrchr (file_name, '/'); | 
|  |  | 
|  | if (lastslash != NULL) | 
|  | { | 
|  | if (lastslash == file_name) | 
|  | { | 
|  | /* "/foobar" => crdir + "foobar".  */ | 
|  | *name = (char *) file_name + 1; | 
|  | return (*use_init_port) (INIT_PORT_CRDIR, &addref); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* "/dir1/dir2/.../file".  */ | 
|  | char dirname[lastslash - file_name + 1]; | 
|  | memcpy (dirname, file_name, lastslash - file_name); | 
|  | dirname[lastslash - file_name] = '\0'; | 
|  | *name = (char *) lastslash + 1; | 
|  | return | 
|  | __hurd_file_name_lookup (use_init_port, get_dtable_port, lookup, | 
|  | dirname, 0, 0, dir); | 
|  | } | 
|  | } | 
|  | else if (file_name[0] == '\0') | 
|  | return ENOENT; | 
|  | else | 
|  | { | 
|  | /* "foobar" => cwdir + "foobar".  */ | 
|  | *name = (char *) file_name; | 
|  | return (*use_init_port) (INIT_PORT_CWDIR, &addref); | 
|  | } | 
|  | } | 
|  | weak_alias (__hurd_file_name_split, hurd_file_name_split) | 
|  |  | 
|  | /* This is the same as hurd_file_name_split, except that it ignores | 
|  | trailing slashes (so *NAME is never "").  */ | 
|  | error_t | 
|  | __hurd_directory_name_split (error_t (*use_init_port) | 
|  | (int which, error_t (*operate) (file_t)), | 
|  | file_t (*get_dtable_port) (int fd), | 
|  | error_t (*lookup) | 
|  | (file_t dir, char *name, int flags, mode_t mode, | 
|  | retry_type *do_retry, string_t retry_name, | 
|  | mach_port_t *result), | 
|  | const char *file_name, | 
|  | file_t *dir, char **name) | 
|  | { | 
|  | error_t addref (file_t crdir) | 
|  | { | 
|  | *dir = crdir; | 
|  | return __mach_port_mod_refs (__mach_task_self (), | 
|  | crdir, MACH_PORT_RIGHT_SEND, +1); | 
|  | } | 
|  |  | 
|  | const char *lastslash = strrchr (file_name, '/'); | 
|  |  | 
|  | if (lastslash != NULL && lastslash[1] == '\0') | 
|  | { | 
|  | /* Trailing slash doesn't count.  Look back further.  */ | 
|  |  | 
|  | /* Back up over all trailing slashes.  */ | 
|  | while (lastslash > file_name && *lastslash == '/') | 
|  | --lastslash; | 
|  |  | 
|  | /* Find the last one earlier in the string, before the trailing ones.  */ | 
|  | lastslash = __memrchr (file_name, '/', lastslash - file_name); | 
|  | } | 
|  |  | 
|  | if (lastslash != NULL) | 
|  | { | 
|  | if (lastslash == file_name) | 
|  | { | 
|  | /* "/foobar" => crdir + "foobar".  */ | 
|  | *name = (char *) file_name + 1; | 
|  | return (*use_init_port) (INIT_PORT_CRDIR, &addref); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* "/dir1/dir2/.../file".  */ | 
|  | char dirname[lastslash - file_name + 1]; | 
|  | memcpy (dirname, file_name, lastslash - file_name); | 
|  | dirname[lastslash - file_name] = '\0'; | 
|  | *name = (char *) lastslash + 1; | 
|  | return | 
|  | __hurd_file_name_lookup (use_init_port, get_dtable_port, lookup, | 
|  | dirname, 0, 0, dir); | 
|  | } | 
|  | } | 
|  | else if (file_name[0] == '\0') | 
|  | return ENOENT; | 
|  | else | 
|  | { | 
|  | /* "foobar" => cwdir + "foobar".  */ | 
|  | *name = (char *) file_name; | 
|  | return (*use_init_port) (INIT_PORT_CWDIR, &addref); | 
|  | } | 
|  | } | 
|  | weak_alias (__hurd_directory_name_split, hurd_directory_name_split) | 
|  |  | 
|  |  | 
|  | file_t | 
|  | __file_name_lookup (const char *file_name, int flags, mode_t mode) | 
|  | { | 
|  | error_t err; | 
|  | file_t result; | 
|  |  | 
|  | err = __hurd_file_name_lookup (&_hurd_ports_use, &__getdport, 0, | 
|  | file_name, flags, mode & ~_hurd_umask, | 
|  | &result); | 
|  |  | 
|  | return err ? (__hurd_fail (err), MACH_PORT_NULL) : result; | 
|  | } | 
|  | weak_alias (__file_name_lookup, file_name_lookup) | 
|  |  | 
|  |  | 
|  | file_t | 
|  | __file_name_split (const char *file_name, char **name) | 
|  | { | 
|  | error_t err; | 
|  | file_t result; | 
|  |  | 
|  | err = __hurd_file_name_split (&_hurd_ports_use, &__getdport, 0, | 
|  | file_name, &result, name); | 
|  |  | 
|  | return err ? (__hurd_fail (err), MACH_PORT_NULL) : result; | 
|  | } | 
|  | weak_alias (__file_name_split, file_name_split) | 
|  |  | 
|  | file_t | 
|  | __directory_name_split (const char *directory_name, char **name) | 
|  | { | 
|  | error_t err; | 
|  | file_t result; | 
|  |  | 
|  | err = __hurd_directory_name_split (&_hurd_ports_use, &__getdport, 0, | 
|  | directory_name, &result, name); | 
|  |  | 
|  | return err ? (__hurd_fail (err), MACH_PORT_NULL) : result; | 
|  | } | 
|  | weak_alias (__directory_name_split, directory_name_split) | 
|  |  | 
|  |  | 
|  | file_t | 
|  | __file_name_lookup_under (file_t startdir, | 
|  | const char *file_name, int flags, mode_t mode) | 
|  | { | 
|  | error_t err; | 
|  | file_t result; | 
|  |  | 
|  | error_t use_init_port (int which, error_t (*operate) (mach_port_t)) | 
|  | { | 
|  | return (which == INIT_PORT_CWDIR ? (*operate) (startdir) : | 
|  | _hurd_ports_use (which, operate)); | 
|  | } | 
|  |  | 
|  | err = __hurd_file_name_lookup (&use_init_port, &__getdport, 0, | 
|  | file_name, flags, mode & ~_hurd_umask, | 
|  | &result); | 
|  |  | 
|  | return err ? (__hurd_fail (err), MACH_PORT_NULL) : result; | 
|  | } | 
|  | weak_alias (__file_name_lookup_under, file_name_lookup_under) |