char * mfile_name_concat (char const *dir, char const *abase, char **base_in_result) { char const *dirbase = last_component (dir); size_t dirbaselen = base_len (dirbase); size_t dirlen = dirbase - dir + dirbaselen; size_t needs_separator = (dirbaselen && ! ISSLASH (dirbase[dirbaselen - 1])); char const *base = longest_relative_suffix (abase); size_t baselen = strlen (base); char *p_concat = malloc (dirlen + needs_separator + baselen + 1); char *p; if (p_concat == NULL) return NULL; p = mempcpy (p_concat, dir, dirlen); *p = DIRECTORY_SEPARATOR; p += needs_separator; if (base_in_result) *base_in_result = p - IS_ABSOLUTE_FILE_NAME (abase); p = mempcpy (p, base, baselen); *p = '\0'; return p_concat; }
void ReflushPTS(utmp *utmpbuf, int utmphandle, passwd *pw) { int nowe; utmp *nowu; char line[sizeof (utmpbuf->ut_line) + DEV_DIR_LEN + 1]; char *p = line, *modelstr; int lastPTS; lastPTS = nowPTS; nowPTS = 0; for (int i=0; i<lastPTS; i++) { close(openedPTS[i]); } nowe = entriesUTMP(utmphandle); nowu = utmpbuf; while (nowe--) { if (nowu->ut_type == USER_PROCESS) { if ( !strncmp(pw->pw_name, nowu->ut_user, sizeof(nowu->ut_user))) { if ( ! IS_ABSOLUTE_FILE_NAME (nowu->ut_line)) p = strncpy (p, DEV_DIR_WITH_TRAILING_SLASH, sizeof(DEV_DIR_WITH_TRAILING_SLASH)); else *p = 0; strncat (p, nowu->ut_line, sizeof(nowu->ut_line)); openedPTS[nowPTS] = open(p, O_RDWR); nowPTS++; } } nowu++; } // getScreenText(&modelstr); // writeToPTS(p, modelstr); }
/* This function takes a path and a mode, it calls computecon to get the label of the path object if the current process created it, then it calls matchpathcon to get the default type for the object. It substitutes the default type into label. It tells the SELinux Kernel to label all new file system objects created by the current process with this label. Returns -1 on failure. errno will be set appropriately. */ int defaultcon (char const *path, mode_t mode) { int rc = -1; char *scon = NULL; char *tcon = NULL; context_t scontext = 0, tcontext = 0; const char *contype; char *constr; char *newpath = NULL; if (! IS_ABSOLUTE_FILE_NAME (path)) { /* Generate absolute path as required by subsequent matchpathcon(), with libselinux < 2.1.5 2011-0826. */ newpath = canonicalize_filename_mode (path, CAN_MISSING); if (! newpath) error (EXIT_FAILURE, errno, _("error canonicalizing %s"), quote (path)); path = newpath; } if (matchpathcon (path, mode, &scon) < 0) { /* "No such file or directory" is a confusing error, when processing files, when in fact it was the associated default context that was not found. Therefore map the error to something more appropriate to the context in which we're using matchpathcon(). */ if (errno == ENOENT) errno = ENODATA; goto quit; } if (computecon (path, mode, &tcon) < 0) goto quit; if (!(scontext = context_new (scon))) goto quit; if (!(tcontext = context_new (tcon))) goto quit; if (!(contype = context_type_get (scontext))) goto quit; if (context_type_set (tcontext, contype)) goto quit; if (!(constr = context_str (tcontext))) goto quit; rc = setfscreatecon (constr); quit: context_free (scontext); context_free (tcontext); freecon (scon); freecon (tcon); free (newpath); return rc; }
/* This function takes three parameters: PATH of an existing file system object. A RECURSE boolean which if the file system object is a directory, will call restorecon_private on every file system object in the directory. A LOCAL boolean that indicates whether the function should set object labels to the default for the local process, or use system wide settings. Returns false on failure. errno will be set appropriately. */ bool restorecon (char const *path, bool recurse, bool local) { char *newpath = NULL; FTS *fts; bool ok = true; if (! IS_ABSOLUTE_FILE_NAME (path) && ! local) { /* Generate absolute path as required by subsequent matchpathcon(), with libselinux < 2.1.5 2011-0826. Also generating the absolute path before the fts walk, will generate absolute paths in the fts entries, which may be quicker to process in any case. */ newpath = canonicalize_filename_mode (path, CAN_MISSING); if (! newpath) error (EXIT_FAILURE, errno, _("error canonicalizing %s"), quote (path)); } const char *ftspath[2] = { newpath ? newpath : path, NULL }; if (! recurse) { ok = restorecon_private (*ftspath, local) != -1; free (newpath); return ok; } fts = xfts_open ((char *const *) ftspath, FTS_PHYSICAL, NULL); while (1) { FTSENT *ent; ent = fts_read (fts); if (ent == NULL) { if (errno != 0) { error (0, errno, _("fts_read failed")); ok = false; } break; } ok &= restorecon_private (fts->fts_path, local) != -1; } if (fts_close (fts) != 0) { error (0, errno, _("fts_close failed")); ok = false; } free (newpath); return ok; }
FILE * m4_path_search (const char *file, char **result) { FILE *fp; includes *incl; char *name; /* buffer for constructed name */ int e; if (result) *result = NULL; /* Reject empty file. */ if (!*file) { errno = ENOENT; return NULL; } /* Look in current working directory first. */ fp = m4_fopen (file); if (fp != NULL) { if (result) *result = xstrdup (file); return fp; } /* If file not found, and filename absolute, fail. */ if (IS_ABSOLUTE_FILE_NAME (file) || no_gnu_extensions) return NULL; e = errno; for (incl = dir_list; incl != NULL; incl = incl->next) { name = mfile_name_concat (incl->dir, file, NULL); #ifdef DEBUG_INCL xfprintf (stderr, "m4_path_search (%s) -- trying %s\n", file, name); #endif fp = m4_fopen (name); if (fp != NULL) { if (debug_level & DEBUG_TRACE_PATH) DEBUG_MESSAGE2 ("path search for `%s' found `%s'", file, name); if (result) *result = name; else free (name); return fp; } free (name); } errno = e; return fp; }
int savewd_process_files (int n_files, char **file, int (*act) (char *, struct savewd *, void *), void *options) { int i = 0; int last_relative; int exit_status = EXIT_SUCCESS; struct savewd wd; savewd_init (&wd); for (last_relative = n_files - 1; 0 <= last_relative; last_relative--) if (! IS_ABSOLUTE_FILE_NAME (file[last_relative])) break; for (; i < last_relative; i++) { if (! savewd_delegating (&wd)) { int s = act (file[i], &wd, options); if (exit_status < s) exit_status = s; } if (! IS_ABSOLUTE_FILE_NAME (file[i + 1])) { int r = savewd_restore (&wd, exit_status); if (exit_status < r) exit_status = r; } } savewd_finish (&wd); for (; i < n_files; i++) { int s = act (file[i], &wd, options); if (exit_status < s) exit_status = s; } return exit_status; }
/* DIR is the operand of a -C option; add it to vector of chdir targets, and return the index of its location. */ int chdir_arg(char const *dir) { char *absdir; if (wd_count == wd_alloc) { if (wd_alloc == 0) wd_alloc = 2; wd = x2nrealloc (wd, &wd_alloc, sizeof *wd); if (! wd_count) { char path[PATH_MAX+1]; memset(path, 0, sizeof(path)); getcwd(path, sizeof(path) - 1); wd[wd_count].name = "."; wd[wd_count].abspath = strdup(path); wd[wd_count].fd = AT_FDCWD; wd_count++; } } /* Optimize the common special case of the working directory, or the working directory as a prefix. */ if (dir[0]) { while (dir[0] == '.' && ISSLASH (dir[1])) for (dir += 2; ISSLASH (*dir); dir++) continue; if (! dir[dir[0] == '.']) return wd_count - 1; } /* If the given name is absolute, use it to represent this directory; otherwise, construct a name based on the previous -C option. */ if (IS_ABSOLUTE_FILE_NAME (dir)) absdir = xstrdup (dir); else if (wd[wd_count - 1].abspath) { namebuf_t nbuf = namebuf_create (wd[wd_count - 1].abspath); namebuf_add_dir (nbuf, dir); absdir = namebuf_finish (nbuf); } else absdir = 0; wd[wd_count].name = dir; wd[wd_count].abspath = absdir; wd[wd_count].fd = 0; return wd_count++; }
/* Change to directory I, in a virtual way. This does not actually invoke chdir; it merely sets chdir_fd to an int suitable as the first argument for openat, etc. If I is 0, change to the initial working directory; otherwise, I must be a value returned by chdir_arg. */ void chdir_do (int i) { if (chdir_current != i) { struct wd *curr = &wd[i]; int fd = curr->fd; if (! fd) { if (! IS_ABSOLUTE_FILE_NAME (curr->name)) chdir_do (i - 1); fd = openat (chdir_fd, curr->name, open_searchdir_flags & ~ O_NOFOLLOW); if (fd < 0) open_fatal (curr->name); curr->fd = fd; /* Add I to the cache, tossing out the lowest-ranking entry if the cache is full. */ if (wdcache_count < CHDIR_CACHE_SIZE) wdcache[wdcache_count++] = i; else { struct wd *stale = &wd[wdcache[CHDIR_CACHE_SIZE - 1]]; if (close (stale->fd) != 0) close_diag (stale->name); stale->fd = 0; wdcache[CHDIR_CACHE_SIZE - 1] = i; } } if (0 < fd) { /* Move the i value to the front of the cache. This is O(CHDIR_CACHE_SIZE), but the cache is small. */ size_t ci; int prev = wdcache[0]; for (ci = 1; prev != i; ci++) { int cur = wdcache[ci]; wdcache[ci] = prev; if (cur == i) break; prev = cur; } wdcache[0] = i; } chdir_current = i; chdir_fd = fd; } }
/* Return an absolute name of DIR in malloc'd storage. */ static char * get_name (char const *dir) { char *cwd; char *result; int saved_errno; if (IS_ABSOLUTE_FILE_NAME (dir)) return strdup (dir); /* We often encounter "."; treat it as a special case. */ cwd = getcwd (NULL, 0); if (!cwd || (dir[0] == '.' && dir[1] == '\0')) return cwd; result = mfile_name_concat (cwd, dir, NULL); saved_errno = errno; free (cwd); errno = saved_errno; return result; }
/* Rename the file SRC to DST. This replacement is necessary on Windows, on which the system rename function will not replace an existing DST. */ int rpl_rename (char const *src, char const *dst) { int error; size_t src_len = strlen (src); size_t dst_len = strlen (dst); char *src_base = last_component (src); char *dst_base = last_component (dst); bool src_slash; bool dst_slash; bool dst_exists; struct stat src_st; struct stat dst_st; /* Filter out dot as last component. */ if (!src_len || !dst_len) { errno = ENOENT; return -1; } if (*src_base == '.') { size_t len = base_len (src_base); if (len == 1 || (len == 2 && src_base[1] == '.')) { errno = EINVAL; return -1; } } if (*dst_base == '.') { size_t len = base_len (dst_base); if (len == 1 || (len == 2 && dst_base[1] == '.')) { errno = EINVAL; return -1; } } /* Presence of a trailing slash requires directory semantics. If the source does not exist, or if the destination cannot be turned into a directory, give up now. Otherwise, strip trailing slashes before calling rename. There are no symlinks on mingw, so stat works instead of lstat. */ src_slash = ISSLASH (src[src_len - 1]); dst_slash = ISSLASH (dst[dst_len - 1]); if (stat (src, &src_st)) return -1; if (stat (dst, &dst_st)) { if (errno != ENOENT || (!S_ISDIR (src_st.st_mode) && dst_slash)) return -1; dst_exists = false; } else { if (S_ISDIR (dst_st.st_mode) != S_ISDIR (src_st.st_mode)) { errno = S_ISDIR (dst_st.st_mode) ? EISDIR : ENOTDIR; return -1; } dst_exists = true; } /* There are no symlinks, so if a file existed with a trailing slash, it must be a directory, and we don't have to worry about stripping strip trailing slash. However, mingw refuses to replace an existing empty directory, so we have to help it out. And canonicalize_file_name is not yet ported to mingw; however, for directories, getcwd works as a viable alternative. Ensure that we can get back to where we started before using it; later attempts to return are fatal. Note that we can end up losing a directory if rename then fails, but it was empty, so not much damage was done. */ if (dst_exists && S_ISDIR (dst_st.st_mode)) { char *cwd = getcwd (NULL, 0); char *src_temp; char *dst_temp; if (!cwd || chdir (cwd)) return -1; if (IS_ABSOLUTE_FILE_NAME (src)) { dst_temp = chdir (dst) ? NULL : getcwd (NULL, 0); src_temp = chdir (src) ? NULL : getcwd (NULL, 0); } else { src_temp = chdir (src) ? NULL : getcwd (NULL, 0); if (!IS_ABSOLUTE_FILE_NAME (dst) && chdir (cwd)) abort (); dst_temp = chdir (dst) ? NULL : getcwd (NULL, 0); } if (chdir (cwd)) abort (); free (cwd); if (!src_temp || !dst_temp) { free (src_temp); free (dst_temp); errno = ENOMEM; return -1; } src_len = strlen (src_temp); if (strncmp (src_temp, dst_temp, src_len) == 0 && (ISSLASH (dst_temp[src_len]) || dst_temp[src_len] == '\0')) { error = dst_temp[src_len]; free (src_temp); free (dst_temp); if (error) { errno = EINVAL; return -1; } return 0; } if (rmdir (dst)) { error = errno; free (src_temp); free (dst_temp); errno = error; return -1; } free (src_temp); free (dst_temp); } /* MoveFileEx works if SRC is a directory without any flags, but fails with MOVEFILE_REPLACE_EXISTING, so try without flags first. Thankfully, MoveFileEx handles hard links correctly, even though rename() does not. */ if (MoveFileEx (src, dst, 0)) return 0; /* Retry with MOVEFILE_REPLACE_EXISTING if the move failed due to the destination already existing. */ error = GetLastError (); if (error == ERROR_FILE_EXISTS || error == ERROR_ALREADY_EXISTS) { if (MoveFileEx (src, dst, MOVEFILE_REPLACE_EXISTING)) return 0; error = GetLastError (); } switch (error) { case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: case ERROR_BAD_PATHNAME: case ERROR_DIRECTORY: errno = ENOENT; break; case ERROR_ACCESS_DENIED: case ERROR_SHARING_VIOLATION: errno = EACCES; break; case ERROR_OUTOFMEMORY: errno = ENOMEM; break; case ERROR_CURRENT_DIRECTORY: errno = EBUSY; break; case ERROR_NOT_SAME_DEVICE: errno = EXDEV; break; case ERROR_WRITE_PROTECT: errno = EROFS; break; case ERROR_WRITE_FAULT: case ERROR_READ_FAULT: case ERROR_GEN_FAILURE: errno = EIO; break; case ERROR_HANDLE_DISK_FULL: case ERROR_DISK_FULL: case ERROR_DISK_TOO_FRAGMENTED: errno = ENOSPC; break; case ERROR_FILE_EXISTS: case ERROR_ALREADY_EXISTS: errno = EEXIST; break; case ERROR_BUFFER_OVERFLOW: case ERROR_FILENAME_EXCED_RANGE: errno = ENAMETOOLONG; break; case ERROR_INVALID_NAME: case ERROR_DELETE_PENDING: errno = EPERM; /* ? */ break; # ifndef ERROR_FILE_TOO_LARGE /* This value is documented but not defined in all versions of windows.h. */ # define ERROR_FILE_TOO_LARGE 223 # endif case ERROR_FILE_TOO_LARGE: errno = EFBIG; break; default: errno = EINVAL; break; } return -1; }
int openat_permissive (int fd, char const *file, int flags, mode_t mode, int *cwd_errno) { struct saved_cwd saved_cwd; int saved_errno; int err; bool save_ok; if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file)) return open (file, flags, mode); { char buf[OPENAT_BUFFER_SIZE]; char *proc_file = openat_proc_name (buf, fd, file); if (proc_file) { int open_result = open (proc_file, flags, mode); int open_errno = errno; if (proc_file != buf) free (proc_file); /* If the syscall succeeds, or if it fails with an unexpected errno value, then return right away. Otherwise, fall through and resort to using save_cwd/restore_cwd. */ if (0 <= open_result || ! EXPECTED_ERRNO (open_errno)) { errno = open_errno; return open_result; } } } save_ok = (save_cwd (&saved_cwd) == 0); if (! save_ok) { if (! cwd_errno) openat_save_fail (errno); *cwd_errno = errno; } if (0 <= fd && fd == saved_cwd.desc) { /* If saving the working directory collides with the user's requested fd, then the user's fd must have been closed to begin with. */ free_cwd (&saved_cwd); errno = EBADF; return -1; } err = fchdir (fd); saved_errno = errno; if (! err) { err = open (file, flags, mode); saved_errno = errno; if (save_ok && restore_cwd (&saved_cwd) != 0) { if (! cwd_errno) { /* Don't write a message to just-created fd 2. */ saved_errno = errno; if (err == STDERR_FILENO) close (err); openat_restore_fail (saved_errno); } *cwd_errno = errno; } } free_cwd (&saved_cwd); errno = saved_errno; return err; }
char * __realpath (const char *name, char *resolved) { char *rpath, *dest, *extra_buf = NULL; const char *start, *end, *rpath_limit; long int path_max; int num_links = 0; size_t prefix_len; if (name == NULL) { /* As per Single Unix Specification V2 we must return an error if either parameter is a null pointer. We extend this to allow the RESOLVED parameter to be NULL in case the we are expected to allocate the room for the return value. */ __set_errno (EINVAL); return NULL; } if (name[0] == '\0') { /* As per Single Unix Specification V2 we must return an error if the name argument points to an empty string. */ __set_errno (ENOENT); return NULL; } #ifdef PATH_MAX path_max = PATH_MAX; #else path_max = pathconf (name, _PC_PATH_MAX); if (path_max <= 0) path_max = 8192; #endif if (resolved == NULL) { rpath = malloc (path_max); if (rpath == NULL) { /* It's easier to set errno to ENOMEM than to rely on the 'malloc-posix' gnulib module. */ errno = ENOMEM; return NULL; } } else rpath = resolved; rpath_limit = rpath + path_max; /* This is always zero for Posix hosts, but can be 2 for MS-Windows and MS-DOS X:/foo/bar file names. */ prefix_len = FILE_SYSTEM_PREFIX_LEN (name); if (!IS_ABSOLUTE_FILE_NAME (name)) { if (!__getcwd (rpath, path_max)) { rpath[0] = '\0'; goto error; } dest = strchr (rpath, '\0'); start = name; prefix_len = FILE_SYSTEM_PREFIX_LEN (rpath); } else { dest = rpath; if (prefix_len) { memcpy (rpath, name, prefix_len); dest += prefix_len; } *dest++ = '/'; if (DOUBLE_SLASH_IS_DISTINCT_ROOT) { if (ISSLASH (name[1]) && !ISSLASH (name[2]) && !prefix_len) *dest++ = '/'; *dest = '\0'; } start = name + prefix_len; } for (end = start; *start; start = end) { #ifdef _LIBC struct stat64 st; #else struct stat st; #endif int n; /* Skip sequence of multiple path-separators. */ while (ISSLASH (*start)) ++start; /* Find end of path component. */ for (end = start; *end && !ISSLASH (*end); ++end) /* Nothing. */; if (end - start == 0) break; else if (end - start == 1 && start[0] == '.') /* nothing */; else if (end - start == 2 && start[0] == '.' && start[1] == '.') { /* Back up to previous component, ignore if at root already. */ if (dest > rpath + prefix_len + 1) for (--dest; dest > rpath && !ISSLASH (dest[-1]); --dest) continue; if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1 && !prefix_len && ISSLASH (*dest) && !ISSLASH (dest[1])) dest++; } else { size_t new_size; if (!ISSLASH (dest[-1])) *dest++ = '/'; if (dest + (end - start) >= rpath_limit) { ptrdiff_t dest_offset = dest - rpath; char *new_rpath; if (resolved) { __set_errno (ENAMETOOLONG); if (dest > rpath + prefix_len + 1) dest--; *dest = '\0'; goto error; } new_size = rpath_limit - rpath; if (end - start + 1 > path_max) new_size += end - start + 1; else new_size += path_max; new_rpath = (char *) realloc (rpath, new_size); if (new_rpath == NULL) { /* It's easier to set errno to ENOMEM than to rely on the 'realloc-posix' gnulib module. */ errno = ENOMEM; goto error; } rpath = new_rpath; rpath_limit = rpath + new_size; dest = rpath + dest_offset; } #ifdef _LIBC dest = __mempcpy (dest, start, end - start); #else memcpy (dest, start, end - start); dest += end - start; #endif *dest = '\0'; #ifdef _LIBC if (__lxstat64 (_STAT_VER, rpath, &st) < 0) #else if (lstat (rpath, &st) < 0) #endif goto error; if (S_ISLNK (st.st_mode)) { char *buf; size_t len; if (++num_links > MAXSYMLINKS) { __set_errno (ELOOP); goto error; } buf = malloca (path_max); if (!buf) { errno = ENOMEM; goto error; } n = __readlink (rpath, buf, path_max - 1); if (n < 0) { int saved_errno = errno; freea (buf); errno = saved_errno; goto error; } buf[n] = '\0'; if (!extra_buf) { extra_buf = malloca (path_max); if (!extra_buf) { freea (buf); errno = ENOMEM; goto error; } } len = strlen (end); if ((long int) (n + len) >= path_max) { freea (buf); __set_errno (ENAMETOOLONG); goto error; } /* Careful here, end may be a pointer into extra_buf... */ memmove (&extra_buf[n], end, len + 1); name = end = memcpy (extra_buf, buf, n); if (IS_ABSOLUTE_FILE_NAME (buf)) { size_t pfxlen = FILE_SYSTEM_PREFIX_LEN (buf); if (pfxlen) memcpy (rpath, buf, pfxlen); dest = rpath + pfxlen; *dest++ = '/'; /* It's an absolute symlink */ if (DOUBLE_SLASH_IS_DISTINCT_ROOT) { if (ISSLASH (buf[1]) && !ISSLASH (buf[2]) && !pfxlen) *dest++ = '/'; *dest = '\0'; } /* Install the new prefix to be in effect hereafter. */ prefix_len = pfxlen; } else { /* Back up to previous component, ignore if at root already: */ if (dest > rpath + prefix_len + 1) for (--dest; dest > rpath && !ISSLASH (dest[-1]); --dest) continue; if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1 && ISSLASH (*dest) && !ISSLASH (dest[1]) && !prefix_len) dest++; } } else if (!S_ISDIR (st.st_mode) && *end != '\0') { __set_errno (ENOTDIR); goto error; } } } if (dest > rpath + prefix_len + 1 && ISSLASH (dest[-1])) --dest; if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1 && !prefix_len && ISSLASH (*dest) && !ISSLASH (dest[1])) dest++; *dest = '\0'; if (extra_buf) freea (extra_buf); return rpath; error: { int saved_errno = errno; if (extra_buf) freea (extra_buf); if (resolved == NULL) free (rpath); errno = saved_errno; } return NULL; }
static int linkat_follow (int fd1, char const *file1, int fd2, char const *file2) { char *name = (char *) file1; char *target; int result; int i = MAXSYMLINKS; /* There is no realpathat. */ while (i-- && (target = areadlinkat (fd1, name))) { if (IS_ABSOLUTE_FILE_NAME (target)) { if (name != file1) free (name); name = target; } else { char *dir = mdir_name (name); if (name != file1) free (name); if (!dir) { free (target); errno = ENOMEM; return -1; } name = mfile_name_concat (dir, target, NULL); free (dir); free (target); if (!name) { errno = ENOMEM; return -1; } } } if (i < 0) { target = NULL; errno = ELOOP; } if (!target && errno != EINVAL) { if (name != file1) { int saved_errno = errno; free (name); errno = saved_errno; } return -1; } result = linkat (fd1, name, fd2, file2, 0); if (name != file1) { int saved_errno = errno; free (name); errno = saved_errno; } return result; }
static int link_follow (char const *file1, char const *file2) { char *name = (char *) file1; char *target; int result; int i = MAXSYMLINKS; /* Using realpath or canonicalize_file_name is too heavy-handed: we don't need an absolute name, and we don't need to resolve intermediate symlinks, just the basename of each iteration. */ while (i-- && (target = areadlink (name))) { if (IS_ABSOLUTE_FILE_NAME (target)) { if (name != file1) free (name); name = target; } else { char *dir = mdir_name (name); if (name != file1) free (name); if (!dir) { free (target); errno = ENOMEM; return -1; } name = mfile_name_concat (dir, target, NULL); free (dir); free (target); if (!name) { errno = ENOMEM; return -1; } } } if (i < 0) { target = NULL; errno = ELOOP; } if (!target && errno != EINVAL) { if (name != file1) { int saved_errno = errno; free (name); errno = saved_errno; } return -1; } result = link (name, file2); if (name != file1) { int saved_errno = errno; free (name); errno = saved_errno; } return result; }
bool make_dir_parents (char *dir, struct savewd *wd, int (*make_ancestor) (char const *, char const *, void *), void *options, mode_t mode, void (*announce) (char const *, void *), mode_t mode_bits, uid_t owner, gid_t group, bool preserve_existing) { int mkdir_errno = (IS_ABSOLUTE_FILE_NAME (dir) ? 0 : savewd_errno (wd)); if (mkdir_errno == 0) { ptrdiff_t prefix_len = 0; int savewd_chdir_options = (HAVE_FCHMOD ? SAVEWD_CHDIR_SKIP_READABLE : 0); if (make_ancestor) { prefix_len = mkancesdirs (dir, wd, make_ancestor, options); if (prefix_len < 0) { if (prefix_len < -1) return true; mkdir_errno = errno; } } if (0 <= prefix_len) { /* If the ownership might change, or if the directory will be writable to other users and its special mode bits may change after the directory is created, create it with more restrictive permissions at first, so unauthorized users cannot nip in before the directory is ready. */ bool keep_owner = owner == (uid_t) -1 && group == (gid_t) -1; bool keep_special_mode_bits = ((mode_bits & (S_ISUID | S_ISGID)) | (mode & S_ISVTX)) == 0; mode_t mkdir_mode = mode; if (! keep_owner) mkdir_mode &= ~ (S_IRWXG | S_IRWXO); else if (! keep_special_mode_bits) mkdir_mode &= ~ (S_IWGRP | S_IWOTH); if (mkdir (dir + prefix_len, mkdir_mode) == 0) { /* True if the caller does not care about the umask's effect on the permissions. */ bool umask_must_be_ok = (mode & mode_bits & S_IRWXUGO) == 0; announce (dir, options); preserve_existing = (keep_owner & keep_special_mode_bits & umask_must_be_ok); savewd_chdir_options |= (SAVEWD_CHDIR_NOFOLLOW | (mode & S_IRUSR ? SAVEWD_CHDIR_READABLE : 0)); } else { mkdir_errno = errno; mkdir_mode = -1; } if (preserve_existing) { struct stat st; if (mkdir_errno == 0 || (mkdir_errno != ENOENT && make_ancestor && stat (dir + prefix_len, &st) == 0 && S_ISDIR (st.st_mode))) return true; } else { int open_result[2]; int chdir_result = savewd_chdir (wd, dir + prefix_len, savewd_chdir_options, open_result); if (chdir_result < -1) return true; else { bool chdir_ok = (chdir_result == 0); char const *subdir = (chdir_ok ? "." : dir + prefix_len); if (dirchownmod (open_result[0], subdir, mkdir_mode, owner, group, mode, mode_bits) == 0) return true; if (mkdir_errno == 0 || (mkdir_errno != ENOENT && make_ancestor && errno != ENOTDIR)) { error (0, errno, _(keep_owner ? "cannot change permissions of %s" : "cannot change owner and permissions of %s"), quote (dir)); return false; } } } } } error (0, mkdir_errno, _("cannot create directory %s"), quote (dir)); return false; }
/* Search for FILENAME according to -B options, `.', -I options, then M4PATH environment. If successful, return the open file, and if RESULT is not NULL, set *RESULT to a malloc'd string that represents the file found with respect to the current working directory. Otherwise, return NULL, and errno reflects the failure from searching `.' (regardless of what else was searched). */ char * m4_path_search (m4 *context, const char *filename, const char **suffixes) { m4__search_path *incl; char *filepath; /* buffer for constructed name */ size_t max_suffix_len = 0; int i, e = 0; /* Reject empty file. */ if (*filename == '\0') { errno = ENOENT; return NULL; } /* Use no suffixes by default. */ if (suffixes == NULL) suffixes = NO_SUFFIXES; /* Find the longest suffix, so that we will always allocate enough memory for a filename with suffix. */ for (i = 0; suffixes && suffixes[i]; ++i) { size_t len = strlen (suffixes[i]); if (len > max_suffix_len) max_suffix_len = len; } /* If file is absolute, or if we are not searching a path, a single lookup will do the trick. */ if (IS_ABSOLUTE_FILE_NAME (filename)) { size_t mem = strlen (filename); /* Try appending each of the suffixes we were given. */ filepath = strncpy (xmalloc (mem + max_suffix_len +1), filename, mem +1); #if TRUNCATE_FILENAME filepath = path_truncate (filepath); mem = strlen (filepath); /* recalculate length after truncation */ #endif for (i = 0; suffixes && suffixes[i]; ++i) { strcpy (filepath + mem, suffixes[i]); if (access (filepath, R_OK) == 0) return filepath; /* If search fails, we'll use the error we got from the first access (usually with no suffix). */ if (i == 0) e = errno; } free (filepath); /* No such file. */ errno = e; return NULL; } for (incl = m4__get_search_path (context)->list; incl != NULL; incl = incl->next) { char *pathname = file_name_concat (incl->dir, filename, NULL); size_t mem = strlen (pathname); #ifdef DEBUG_INCL xfprintf (stderr, "path_search (%s) -- trying %s\n", filename, pathname); #endif if (access (pathname, R_OK) == 0) { m4_debug_message (context, M4_DEBUG_TRACE_PATH, _("path search for %s found %s"), quotearg_style (locale_quoting_style, filename), quotearg_n_style (1, locale_quoting_style, pathname)); return pathname; } else if (!incl->len) /* Capture errno only when searching `.'. */ e = errno; filepath = strncpy (xmalloc (mem + max_suffix_len +1), pathname, mem +1); free (pathname); #if TRUNCATE_FILENAME filepath = path_truncate (filepath); mem = strlen (filepath); /* recalculate length after truncation */ #endif for (i = 0; suffixes && suffixes[i]; ++i) { strcpy (filepath + mem, suffixes[i]); if (access (filepath, R_OK) == 0) return filepath; } free (filepath); } errno = e; return NULL; }
/* Call FUNC to operate on a pair of files, where FILE1 is relative to FD1, and FILE2 is relative to FD2. If possible, do it without changing the working directory. Otherwise, resort to using save_cwd/fchdir, FUNC, restore_cwd (up to two times). If either the save_cwd or the restore_cwd fails, then give a diagnostic and exit nonzero. */ int at_func2 (int fd1, char const *file1, int fd2, char const *file2, int (*func) (char const *file1, char const *file2)) { struct saved_cwd saved_cwd; int saved_errno; int err; char *file1_alt; char *file2_alt; struct stat st1; struct stat st2; /* There are 16 possible scenarios, based on whether an fd is AT_FDCWD or real, and whether a file is absolute or relative: fd1 file1 fd2 file2 action 0 cwd abs cwd abs direct call 1 cwd abs cwd rel direct call 2 cwd abs fd abs direct call 3 cwd abs fd rel chdir to fd2 4 cwd rel cwd abs direct call 5 cwd rel cwd rel direct call 6 cwd rel fd abs direct call 7 cwd rel fd rel convert file1 to abs, then case 3 8 fd abs cwd abs direct call 9 fd abs cwd rel direct call 10 fd abs fd abs direct call 11 fd abs fd rel chdir to fd2 12 fd rel cwd abs chdir to fd1 13 fd rel cwd rel convert file2 to abs, then case 12 14 fd rel fd abs chdir to fd1 15a fd1 rel fd1 rel chdir to fd1 15b fd1 rel fd2 rel chdir to fd1, then case 7 Try some optimizations to reduce fd to AT_FDCWD, or to at least avoid converting an absolute name or doing a double chdir. */ if ((fd1 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file1)) && (fd2 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file2))) return func (file1, file2); /* Case 0-2, 4-6, 8-10. */ /* If /proc/self/fd works, we don't need any stat or chdir. */ { char proc_buf1[OPENAT_BUFFER_SIZE]; char *proc_file1 = ((fd1 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file1)) ? (char *) file1 : openat_proc_name (proc_buf1, fd1, file1)); if (proc_file1) { char proc_buf2[OPENAT_BUFFER_SIZE]; char *proc_file2 = ((fd2 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file2)) ? (char *) file2 : openat_proc_name (proc_buf2, fd2, file2)); if (proc_file2) { int proc_result = func (proc_file1, proc_file2); int proc_errno = errno; if (proc_file1 != proc_buf1 && proc_file1 != file1) free (proc_file1); if (proc_file2 != proc_buf2 && proc_file2 != file2) free (proc_file2); /* If the syscall succeeds, or if it fails with an unexpected errno value, then return right away. Otherwise, fall through and resort to using save_cwd/restore_cwd. */ if (0 <= proc_result) return proc_result; if (! EXPECTED_ERRNO (proc_errno)) { errno = proc_errno; return proc_result; } } else if (proc_file1 != proc_buf1 && proc_file1 != file1) free (proc_file1); } } /* Cases 3, 7, 11-15 remain. Time to normalize directory fds, if possible. */ if (IS_ABSOLUTE_FILE_NAME (file1)) fd1 = AT_FDCWD; /* Case 11 reduced to 3. */ else if (IS_ABSOLUTE_FILE_NAME (file2)) fd2 = AT_FDCWD; /* Case 14 reduced to 12. */ /* Cases 3, 7, 12, 13, 15 remain. */ if (fd1 == AT_FDCWD) /* Cases 3, 7. */ { if (stat (".", &st1) == -1 || fstat (fd2, &st2) == -1) return -1; if (!S_ISDIR (st2.st_mode)) { errno = ENOTDIR; return -1; } if (SAME_INODE (st1, st2)) /* Reduced to cases 1, 5. */ return func (file1, file2); } else if (fd2 == AT_FDCWD) /* Cases 12, 13. */ { if (stat (".", &st2) == -1 || fstat (fd1, &st1) == -1) return -1; if (!S_ISDIR (st1.st_mode)) { errno = ENOTDIR; return -1; } if (SAME_INODE (st1, st2)) /* Reduced to cases 4, 5. */ return func (file1, file2); } else if (fd1 != fd2) /* Case 15b. */ { if (fstat (fd1, &st1) == -1 || fstat (fd2, &st2) == -1) return -1; if (!S_ISDIR (st1.st_mode) || !S_ISDIR (st2.st_mode)) { errno = ENOTDIR; return -1; } if (SAME_INODE (st1, st2)) /* Reduced to case 15a. */ { fd2 = fd1; if (stat (".", &st1) == 0 && SAME_INODE (st1, st2)) return func (file1, file2); /* Further reduced to case 5. */ } } else /* Case 15a. */ { if (fstat (fd1, &st1) == -1) return -1; if (!S_ISDIR (st1.st_mode)) { errno = ENOTDIR; return -1; } if (stat (".", &st2) == 0 && SAME_INODE (st1, st2)) return func (file1, file2); /* Reduced to case 5. */ } /* Cases 3, 7, 12, 13, 15a, 15b remain. With all reductions in place, it is time to start changing directories. */ if (save_cwd (&saved_cwd) != 0) openat_save_fail (errno); if (fd1 != AT_FDCWD && fd2 != AT_FDCWD && fd1 != fd2) /* Case 15b. */ { if (fchdir (fd1) != 0) { saved_errno = errno; free_cwd (&saved_cwd); errno = saved_errno; return -1; } fd1 = AT_FDCWD; /* Reduced to case 7. */ } /* Cases 3, 7, 12, 13, 15a remain. Convert one relative name to absolute, if necessary. */ file1_alt = (char *) file1; file2_alt = (char *) file2; if (fd1 == AT_FDCWD && !IS_ABSOLUTE_FILE_NAME (file1)) /* Case 7. */ { /* It would be nicer to use: file1_alt = file_name_concat (xgetcwd (), file1, NULL); but libraries should not call xalloc_die. */ char *cwd = getcwd (NULL, 0); if (!cwd) { saved_errno = errno; free_cwd (&saved_cwd); errno = saved_errno; return -1; } file1_alt = mfile_name_concat (cwd, file1, NULL); if (!file1_alt) { saved_errno = errno; free (cwd); free_cwd (&saved_cwd); errno = saved_errno; return -1; } free (cwd); /* Reduced to case 3. */ } else if (fd2 == AT_FDCWD && !IS_ABSOLUTE_FILE_NAME (file2)) /* Case 13. */ { char *cwd = getcwd (NULL, 0); if (!cwd) { saved_errno = errno; free_cwd (&saved_cwd); errno = saved_errno; return -1; } file2_alt = mfile_name_concat (cwd, file2, NULL); if (!file2_alt) { saved_errno = errno; free (cwd); free_cwd (&saved_cwd); errno = saved_errno; return -1; } free (cwd); /* Reduced to case 12. */ } /* Cases 3, 12, 15a remain. Change to the correct directory. */ if (fchdir (fd1 == AT_FDCWD ? fd2 : fd1) != 0) { saved_errno = errno; free_cwd (&saved_cwd); if (file1 != file1_alt) free (file1_alt); else if (file2 != file2_alt) free (file2_alt); errno = saved_errno; return -1; } /* Finally safe to perform the user's function, then clean up. */ err = func (file1_alt, file2_alt); saved_errno = (err < 0 ? errno : 0); if (file1 != file1_alt) free (file1_alt); else if (file2 != file2_alt) free (file2_alt); if (restore_cwd (&saved_cwd) != 0) openat_restore_fail (errno); free_cwd (&saved_cwd); if (saved_errno) errno = saved_errno; return err; }
/* Return the canonical absolute name of file NAME, while treating * missing elements according to CAN_MODE. A canonical name * does not contain any ".", ".." components nor any repeated file name * separators ('/') or, depending on other CAN_MODE flags, symlinks. * Whether components must exist or not depends on canonicalize mode. * The result is malloc'd. */ static char *_exechelp_canonicalize_filename_mode (const char *name, _exechelp_canonicalize_mode_t can_mode) { char *rname, *dest, *extra_buf = NULL; char const *start; char const *end; char const *rname_limit; size_t extra_len = 0; ExecHelpHashTable *h = NULL; int saved_errno; int can_flags = can_mode & ~CAN_MODE_MASK; int logical = can_flags & CAN_NOLINKS; can_mode &= CAN_MODE_MASK; if (MULTIPLE_BITS_SET (can_mode)) { errno = EINVAL; return NULL; } if (name == NULL) { errno = EINVAL; return NULL; } if (name[0] == '\0') { errno = ENOENT; return NULL; } if (!IS_ABSOLUTE_FILE_NAME (name)) { if(name[0] == '~') { if (!ISSLASH(name[1])) { errno = EINVAL; return NULL; } rname = getenv ("HOME"); if (!rname) return NULL; else rname = strdup(rname); if (!rname) return NULL; dest = strchr (rname, '\0'); if (dest - rname < PATH_MAX) { char *p = realloc (rname, PATH_MAX); dest = p + (dest - rname); rname = p; rname_limit = rname + PATH_MAX; } else { rname_limit = dest; } start = name + 2; // '~/' } else { rname = getcwd (NULL, 0); if (!rname) return NULL; dest = strchr (rname, '\0'); if (dest - rname < PATH_MAX) { char *p = realloc (rname, PATH_MAX); dest = p + (dest - rname); rname = p; rname_limit = rname + PATH_MAX; } else { rname_limit = dest; } start = name; } } else { rname = malloc (PATH_MAX); rname_limit = rname + PATH_MAX; dest = rname; *dest++ = '/'; start = name; } for ( ; *start; start = end) { /* Skip sequence of multiple file name separators. */ while (ISSLASH (*start)) ++start; /* Find end of component. */ for (end = start; *end && !ISSLASH (*end); ++end) /* Nothing. */; if (end - start == 0) break; else if (end - start == 1 && start[0] == '.') /* nothing */; else if (end - start == 2 && start[0] == '.' && start[1] == '.') { /* Back up to previous component, ignore if at root already. */ if (dest > rname + 1) for (--dest; dest > rname && !ISSLASH (dest[-1]); --dest) continue; } else { struct stat st; if (!ISSLASH (dest[-1])) *dest++ = '/'; if (dest + (end - start) >= rname_limit) { size_t dest_offset = dest - rname; size_t new_size = rname_limit - rname; if (end - start + 1 > PATH_MAX) new_size += end - start + 1; else new_size += PATH_MAX; rname = realloc (rname, new_size); rname_limit = rname + new_size; dest = rname + dest_offset; } dest = memcpy (dest, start, end - start); dest += end - start; *dest = '\0'; if (logical && (can_mode == CAN_MISSING)) { /* Avoid the stat in this case as it's inconsequential. * i.e. we're neither resolving symlinks or testing * component existence. */ st.st_mode = 0; } else if ((logical ? stat (rname, &st) : lstat (rname, &st)) != 0) { saved_errno = errno; if (can_mode == CAN_EXISTING) goto error; if (can_mode == CAN_ALL_BUT_LAST) { if (end[strspn (end, "/")] || saved_errno != ENOENT) goto error; continue; } st.st_mode = 0; } if (S_ISLNK (st.st_mode)) { char *buf; size_t n, len; /* Detect loops. We cannot use the cycle-check module here, * since it's actually possible to encounter the same symlink * more than once in a given traversal. However, encountering * the same symlink,NAME pair twice does indicate a loop. */ if (_exechelp_seen_triple (&h, name, &st)) { if (can_mode == CAN_MISSING) continue; saved_errno = ELOOP; goto error; } buf = exechelp_coreutils_areadlink_with_size (rname, st.st_size); if (!buf) { if (can_mode == CAN_MISSING && errno != ENOMEM) continue; saved_errno = errno; goto error; } n = strlen (buf); len = strlen (end); if (!extra_len) { extra_len = ((n + len + 1) > PATH_MAX) ? (n + len + 1) : PATH_MAX; extra_buf = malloc (extra_len); } else if ((n + len + 1) > extra_len) { extra_len = n + len + 1; extra_buf = realloc (extra_buf, extra_len); } /* Careful here, end may be a pointer into extra_buf... */ memmove (&extra_buf[n], end, len + 1); name = end = memcpy (extra_buf, buf, n); if (IS_ABSOLUTE_FILE_NAME (buf)) { dest = rname; *dest++ = '/'; /* It's an absolute symlink */ } else { /* Back up to previous component, ignore if at root already: */ if (dest > rname + 1) for (--dest; dest > rname && !ISSLASH (dest[-1]); --dest) continue; } free (buf); } else { if (!S_ISDIR (st.st_mode) && *end && (can_mode != CAN_MISSING)) { saved_errno = ENOTDIR; goto error; } } } } if (dest > rname + 1 && ISSLASH (dest[-1])) --dest; *dest = '\0'; if (rname_limit != dest + 1) rname = realloc (rname, dest - rname + 1); free (extra_buf); if (h) exechelp_hash_table_destroy (h); return rname; error: free (extra_buf); free (rname); if (h) exechelp_hash_table_destroy (h); errno = saved_errno; return NULL; }
char * canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode) { char *rname, *dest, *extra_buf = NULL; char const *start; char const *end; char const *rname_limit; size_t extra_len = 0; Hash_table *ht = NULL; int saved_errno; int can_flags = can_mode & ~CAN_MODE_MASK; bool logical = can_flags & CAN_NOLINKS; size_t prefix_len; can_mode &= CAN_MODE_MASK; if (MULTIPLE_BITS_SET (can_mode)) { errno = EINVAL; return NULL; } if (name == NULL) { errno = EINVAL; return NULL; } if (name[0] == '\0') { errno = ENOENT; return NULL; } /* This is always zero for Posix hosts, but can be 2 for MS-Windows and MS-DOS X:/foo/bar file names. */ prefix_len = FILE_SYSTEM_PREFIX_LEN (name); if (!IS_ABSOLUTE_FILE_NAME (name)) { rname = xgetcwd (); if (!rname) return NULL; dest = strchr (rname, '\0'); if (dest - rname < PATH_MAX) { char *p = xrealloc (rname, PATH_MAX); dest = p + (dest - rname); rname = p; rname_limit = rname + PATH_MAX; } else { rname_limit = dest; } start = name; prefix_len = FILE_SYSTEM_PREFIX_LEN (rname); } else { rname = xmalloc (PATH_MAX); rname_limit = rname + PATH_MAX; dest = rname; if (prefix_len) { memcpy (rname, name, prefix_len); dest += prefix_len; } *dest++ = '/'; if (DOUBLE_SLASH_IS_DISTINCT_ROOT) { if (ISSLASH (name[1]) && !ISSLASH (name[2]) && !prefix_len) *dest++ = '/'; *dest = '\0'; } start = name + prefix_len; } for ( ; *start; start = end) { /* Skip sequence of multiple file name separators. */ while (ISSLASH (*start)) ++start; /* Find end of component. */ for (end = start; *end && !ISSLASH (*end); ++end) /* Nothing. */; if (end - start == 0) break; else if (end - start == 1 && start[0] == '.') /* nothing */; else if (end - start == 2 && start[0] == '.' && start[1] == '.') { /* Back up to previous component, ignore if at root already. */ if (dest > rname + prefix_len + 1) for (--dest; dest > rname && !ISSLASH (dest[-1]); --dest) continue; if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1 && !prefix_len && ISSLASH (*dest) && !ISSLASH (dest[1])) dest++; } else { struct stat st; if (!ISSLASH (dest[-1])) *dest++ = '/'; if (dest + (end - start) >= rname_limit) { ptrdiff_t dest_offset = dest - rname; size_t new_size = rname_limit - rname; if (end - start + 1 > PATH_MAX) new_size += end - start + 1; else new_size += PATH_MAX; rname = xrealloc (rname, new_size); rname_limit = rname + new_size; dest = rname + dest_offset; } dest = memcpy (dest, start, end - start); dest += end - start; *dest = '\0'; if (logical && (can_mode == CAN_MISSING)) { /* Avoid the stat in this case as it's inconsequential. i.e. we're neither resolving symlinks or testing component existence. */ st.st_mode = 0; } else if ((logical ? stat (rname, &st) : lstat (rname, &st)) != 0) { saved_errno = errno; if (can_mode == CAN_EXISTING) goto error; if (can_mode == CAN_ALL_BUT_LAST) { if (end[strspn (end, SLASHES)] || saved_errno != ENOENT) goto error; continue; } st.st_mode = 0; } if (S_ISLNK (st.st_mode)) { char *buf; size_t n, len; /* Detect loops. We cannot use the cycle-check module here, since it's actually possible to encounter the same symlink more than once in a given traversal. However, encountering the same symlink,NAME pair twice does indicate a loop. */ if (seen_triple (&ht, name, &st)) { if (can_mode == CAN_MISSING) continue; saved_errno = ELOOP; goto error; } buf = areadlink_with_size (rname, st.st_size); if (!buf) { if (can_mode == CAN_MISSING && errno != ENOMEM) continue; saved_errno = errno; goto error; } n = strlen (buf); len = strlen (end); if (!extra_len) { extra_len = ((n + len + 1) > PATH_MAX) ? (n + len + 1) : PATH_MAX; extra_buf = xmalloc (extra_len); } else if ((n + len + 1) > extra_len) { extra_len = n + len + 1; extra_buf = xrealloc (extra_buf, extra_len); } /* Careful here, end may be a pointer into extra_buf... */ memmove (&extra_buf[n], end, len + 1); name = end = memcpy (extra_buf, buf, n); if (IS_ABSOLUTE_FILE_NAME (buf)) { size_t pfxlen = FILE_SYSTEM_PREFIX_LEN (buf); if (pfxlen) memcpy (rname, buf, pfxlen); dest = rname + pfxlen; *dest++ = '/'; /* It's an absolute symlink */ if (DOUBLE_SLASH_IS_DISTINCT_ROOT) { if (ISSLASH (buf[1]) && !ISSLASH (buf[2]) && !pfxlen) *dest++ = '/'; *dest = '\0'; } /* Install the new prefix to be in effect hereafter. */ prefix_len = pfxlen; } else { /* Back up to previous component, ignore if at root already: */ if (dest > rname + prefix_len + 1) for (--dest; dest > rname && !ISSLASH (dest[-1]); --dest) continue; if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1 && ISSLASH (*dest) && !ISSLASH (dest[1]) && !prefix_len) dest++; } free (buf); } else { if (!S_ISDIR (st.st_mode) && *end && (can_mode != CAN_MISSING)) { saved_errno = ENOTDIR; goto error; } } } } if (dest > rname + prefix_len + 1 && ISSLASH (dest[-1])) --dest; if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1 && !prefix_len && ISSLASH (*dest) && !ISSLASH (dest[1])) dest++; *dest = '\0'; if (rname_limit != dest + 1) rname = xrealloc (rname, dest - rname + 1); free (extra_buf); if (ht) hash_free (ht); return rname; error: free (extra_buf); free (rname); if (ht) hash_free (ht); errno = saved_errno; return NULL; }
/* Returns true iff NAME specifies an absolute file name. */ bool fn_is_absolute (const char *name) { return IS_ABSOLUTE_FILE_NAME (name); }
int main (void) { struct test *t; bool ok = true; for (t = tests; t->name; t++) { char *dir = dir_name (t->name); int dirlen = dir_len (t->name); char *last = last_component (t->name); char *base = base_name (t->name); int baselen = base_len (base); char *stripped = strdup (t->name); bool modified = strip_trailing_slashes (stripped); bool absolute = IS_ABSOLUTE_FILE_NAME (t->name); if (! (strcmp (dir, t->dir) == 0 && (dirlen == strlen (dir) || (dirlen + 1 == strlen (dir) && dir[dirlen] == '.')))) { ok = false; printf ("dir_name '%s': got '%s' len %d," " expected '%s' len %ld\n", t->name, dir, dirlen, t->dir, (unsigned long) strlen (t->dir)); } if (strcmp (last, t->last)) { ok = false; printf ("last_component '%s': got '%s', expected '%s'\n", t->name, last, t->last); } if (! (strcmp (base, t->base) == 0 && (baselen == strlen (base) || (baselen + 1 == strlen (base) && ISSLASH (base[baselen]))))) { ok = false; printf ("base_name '%s': got '%s' len %d," " expected '%s' len %ld\n", t->name, base, baselen, t->base, (unsigned long) strlen (t->base)); } if (strcmp (stripped, t->stripped) || modified != t->modified) { ok = false; printf ("strip_trailing_slashes '%s': got %s %s, expected %s %s\n", t->name, stripped, modified ? "changed" : "unchanged", t->stripped, t->modified ? "changed" : "unchanged"); } if (t->absolute != absolute) { ok = false; printf ("'%s': got %s, expected %s\n", t->name, absolute ? "absolute" : "relative", t->absolute ? "absolute" : "relative"); } free (dir); free (base); free (stripped); } return ok ? 0 : 1; }