(_major): Ditto. (device::is_fs_special): New function. * fhandler_disk_file.cc (fhandler_base::fstat_helper): Set the size to 0 for devices rather than reporting the size of the symlink. (fhandler_disk_file::readdir): Use is_fs_special to determine if .lnk should be stripped. * path.cc: Rename symlink_info::is_symlink to symlink_info::issymlink throughout. (symlink_info::isdevice): New field. (path_conv::check): Use 'isdevice' to determine if just-parsed entity is a device rather than relying on non-zero major/minor. (symlink_info::parse_device): Set isdevice to true if we've discovered a device. (symlink_info::check): Clear isdevice field prior to processing. Use isdevice to control debugging output. (symlink_info::set): Set isdevice to false. * path.h (path_conv::is_fs_special): New function. * devices.cc: Regenerate.
		
			
				
	
	
		
			4239 lines
		
	
	
		
			107 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			4239 lines
		
	
	
		
			107 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* path.cc: path support.
 | 
						|
 | 
						|
   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.
 | 
						|
 | 
						|
This file is part of Cygwin.
 | 
						|
 | 
						|
This software is a copyrighted work licensed under the terms of the
 | 
						|
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | 
						|
details. */
 | 
						|
 | 
						|
/* This module's job is to
 | 
						|
   - convert between POSIX and Win32 style filenames,
 | 
						|
   - support the `mount' functionality,
 | 
						|
   - support symlinks for files and directories
 | 
						|
 | 
						|
   Pathnames are handled as follows:
 | 
						|
 | 
						|
   - A \ or : in a path denotes a pure windows spec.
 | 
						|
   - Paths beginning with // (or \\) are not translated (i.e. looked
 | 
						|
     up in the mount table) and are assumed to be UNC path names.
 | 
						|
 | 
						|
   The goal in the above set of rules is to allow both POSIX and Win32
 | 
						|
   flavors of pathnames without either interfering.  The rules are
 | 
						|
   intended to be as close to a superset of both as possible.
 | 
						|
 | 
						|
   Note that you can have more than one path to a file.  The mount
 | 
						|
   table is always prefered when translating Win32 paths to POSIX
 | 
						|
   paths.  Win32 paths in mount table entries may be UNC paths or
 | 
						|
   standard Win32 paths starting with <drive-letter>:
 | 
						|
 | 
						|
   Text vs Binary issues are not considered here in path style
 | 
						|
   decisions, although the appropriate flags are retrieved and
 | 
						|
   stored in various structures.
 | 
						|
 | 
						|
   Removing mounted filesystem support would simplify things greatly,
 | 
						|
   but having it gives us a mechanism of treating disk that lives on a
 | 
						|
   UNIX machine as having UNIX semantics [it allows one to edit a text
 | 
						|
   file on that disk and not have cr's magically appear and perhaps
 | 
						|
   break apps running on UNIX boxes].  It also useful to be able to
 | 
						|
   layout a hierarchy without changing the underlying directories.
 | 
						|
 | 
						|
   The semantics of mounting file systems is not intended to precisely
 | 
						|
   follow normal UNIX systems.
 | 
						|
 | 
						|
   Each DOS drive is defined to have a current directory.  Supporting
 | 
						|
   this would complicate things so for now things are defined so that
 | 
						|
   c: means c:\.
 | 
						|
*/
 | 
						|
 | 
						|
#include "winsup.h"
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <sys/mount.h>
 | 
						|
#include <mntent.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <libgen.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <winioctl.h>
 | 
						|
#include <wingdi.h>
 | 
						|
#include <winuser.h>
 | 
						|
#include <winnls.h>
 | 
						|
#include <winnetwk.h>
 | 
						|
#include <shlobj.h>
 | 
						|
#include <sys/cygwin.h>
 | 
						|
#include <cygwin/version.h>
 | 
						|
#include "cygerrno.h"
 | 
						|
#include "security.h"
 | 
						|
#include "path.h"
 | 
						|
#include "fhandler.h"
 | 
						|
#include "sync.h"
 | 
						|
#include "sigproc.h"
 | 
						|
#include "pinfo.h"
 | 
						|
#include "dtable.h"
 | 
						|
#include "cygheap.h"
 | 
						|
#include "shared_info.h"
 | 
						|
#include "registry.h"
 | 
						|
#include "cygtls.h"
 | 
						|
#include <assert.h>
 | 
						|
 | 
						|
static int normalize_win32_path (const char *, char *, char *&);
 | 
						|
static void slashify (const char *, char *, int);
 | 
						|
static void backslashify (const char *, char *, int);
 | 
						|
 | 
						|
struct symlink_info
 | 
						|
{
 | 
						|
  char contents[CYG_MAX_PATH + 4];
 | 
						|
  char *ext_here;
 | 
						|
  int extn;
 | 
						|
  unsigned pflags;
 | 
						|
  DWORD fileattr;
 | 
						|
  int issymlink;
 | 
						|
  bool ext_tacked_on;
 | 
						|
  int error;
 | 
						|
  bool case_clash;
 | 
						|
  bool isdevice;
 | 
						|
  _major_t major;
 | 
						|
  _minor_t minor;
 | 
						|
  _mode_t mode;
 | 
						|
  int check (char *path, const suffix_info *suffixes, unsigned opt);
 | 
						|
  int set (char *path);
 | 
						|
  bool parse_device (const char *);
 | 
						|
  bool case_check (char *path);
 | 
						|
  int check_sysfile (const char *path, HANDLE h);
 | 
						|
  int check_shortcut (const char *path, HANDLE h);
 | 
						|
  bool set_error (int);
 | 
						|
};
 | 
						|
 | 
						|
muto NO_COPY cwdstuff::cwd_lock;
 | 
						|
 | 
						|
int pcheck_case = PCHECK_RELAXED; /* Determines the case check behaviour. */
 | 
						|
 | 
						|
static const GUID GUID_shortcut
 | 
						|
			= { 0x00021401L, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46 };
 | 
						|
 | 
						|
enum {
 | 
						|
  WSH_FLAG_IDLIST = 0x01,	/* Contains an ITEMIDLIST. */
 | 
						|
  WSH_FLAG_FILE = 0x02,		/* Contains a file locator element. */
 | 
						|
  WSH_FLAG_DESC = 0x04,		/* Contains a description. */
 | 
						|
  WSH_FLAG_RELPATH = 0x08,	/* Contains a relative path. */
 | 
						|
  WSH_FLAG_WD = 0x10,		/* Contains a working dir. */
 | 
						|
  WSH_FLAG_CMDLINE = 0x20,	/* Contains command line args. */
 | 
						|
  WSH_FLAG_ICON = 0x40		/* Contains a custom icon. */
 | 
						|
};
 | 
						|
 | 
						|
struct win_shortcut_hdr
 | 
						|
  {
 | 
						|
    DWORD size;		/* Header size in bytes.  Must contain 0x4c. */
 | 
						|
    GUID magic;		/* GUID of shortcut files. */
 | 
						|
    DWORD flags;	/* Content flags.  See above. */
 | 
						|
 | 
						|
    /* The next fields from attr to icon_no are always set to 0 in Cygwin
 | 
						|
       and U/Win shortcuts. */
 | 
						|
    DWORD attr;	/* Target file attributes. */
 | 
						|
    FILETIME ctime;	/* These filetime items are never touched by the */
 | 
						|
    FILETIME mtime;	/* system, apparently. Values don't matter. */
 | 
						|
    FILETIME atime;
 | 
						|
    DWORD filesize;	/* Target filesize. */
 | 
						|
    DWORD icon_no;	/* Icon number. */
 | 
						|
 | 
						|
    DWORD run;		/* Values defined in winuser.h. Use SW_NORMAL. */
 | 
						|
    DWORD hotkey;	/* Hotkey value. Set to 0.  */
 | 
						|
    DWORD dummy[2];	/* Future extension probably. Always 0. */
 | 
						|
  };
 | 
						|
 | 
						|
/* Determine if path prefix matches current cygdrive */
 | 
						|
#define iscygdrive(path) \
 | 
						|
  (path_prefix_p (mount_table->cygdrive, (path), mount_table->cygdrive_len))
 | 
						|
 | 
						|
#define iscygdrive_device(path) \
 | 
						|
  (isalpha (path[mount_table->cygdrive_len]) && \
 | 
						|
   (path[mount_table->cygdrive_len + 1] == '/' || \
 | 
						|
    !path[mount_table->cygdrive_len + 1]))
 | 
						|
 | 
						|
#define isproc(path) \
 | 
						|
  (path_prefix_p (proc, (path), proc_len))
 | 
						|
 | 
						|
#define isvirtual_dev(devn) \
 | 
						|
  (devn == FH_CYGDRIVE || devn == FH_PROC || devn == FH_REGISTRY \
 | 
						|
   || devn == FH_PROCESS || devn == FH_NETDRIVE )
 | 
						|
 | 
						|
/* Return non-zero if PATH1 is a prefix of PATH2.
 | 
						|
   Both are assumed to be of the same path style and / vs \ usage.
 | 
						|
   Neither may be "".
 | 
						|
   LEN1 = strlen (PATH1).  It's passed because often it's already known.
 | 
						|
 | 
						|
   Examples:
 | 
						|
   /foo/ is a prefix of /foo  <-- may seem odd, but desired
 | 
						|
   /foo is a prefix of /foo/
 | 
						|
   / is a prefix of /foo/bar
 | 
						|
   / is not a prefix of foo/bar
 | 
						|
   foo/ is a prefix foo/bar
 | 
						|
   /foo is not a prefix of /foobar
 | 
						|
*/
 | 
						|
 | 
						|
int
 | 
						|
path_prefix_p (const char *path1, const char *path2, int len1)
 | 
						|
{
 | 
						|
  /* Handle case where PATH1 has trailing '/' and when it doesn't.  */
 | 
						|
  if (len1 > 0 && isdirsep (path1[len1 - 1]))
 | 
						|
    len1--;
 | 
						|
 | 
						|
  if (len1 == 0)
 | 
						|
    return isdirsep (path2[0]) && !isdirsep (path2[1]);
 | 
						|
 | 
						|
  if (isdirsep (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':')
 | 
						|
    return pathnmatch (path1, path2, len1);
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Return non-zero if paths match in first len chars.
 | 
						|
   Check is dependent of the case sensitivity setting. */
 | 
						|
int
 | 
						|
pathnmatch (const char *path1, const char *path2, int len)
 | 
						|
{
 | 
						|
  return pcheck_case == PCHECK_STRICT ? !strncmp (path1, path2, len)
 | 
						|
				      : strncasematch (path1, path2, len);
 | 
						|
}
 | 
						|
 | 
						|
/* Return non-zero if paths match. Check is dependent of the case
 | 
						|
   sensitivity setting. */
 | 
						|
int
 | 
						|
pathmatch (const char *path1, const char *path2)
 | 
						|
{
 | 
						|
  return pcheck_case == PCHECK_STRICT ? !strcmp (path1, path2)
 | 
						|
				      : strcasematch (path1, path2);
 | 
						|
}
 | 
						|
 | 
						|
/* TODO: This function is used in mkdir and rmdir to generate correct
 | 
						|
   error messages in case of paths ending in /. or /.. components.
 | 
						|
   This test should eventually end up in path_conv::check in one way
 | 
						|
   or another.  Right now, normalize_posix_path will just normalize
 | 
						|
   those components away, which changes the semantics.  */
 | 
						|
bool
 | 
						|
has_dot_last_component (const char *dir)
 | 
						|
{
 | 
						|
  /* SUSv3: . and .. are not allowed as last components in various system
 | 
						|
     calls.  Don't test for backslash path separator since that's a Win32
 | 
						|
     path following Win32 rules. */
 | 
						|
  const char *last_comp = strrchr (dir, '/');
 | 
						|
  return last_comp
 | 
						|
  	 && last_comp[1] == '.'
 | 
						|
	 && (last_comp[2] == '\0'
 | 
						|
	     || (last_comp[2] == '.' && last_comp[3] == '\0'));
 | 
						|
}
 | 
						|
 | 
						|
#define isslash(c) ((c) == '/')
 | 
						|
 | 
						|
/* Normalize a POSIX path.
 | 
						|
   All duplicate /'s, except for 2 leading /'s, are deleted.
 | 
						|
   The result is 0 for success, or an errno error value.  */
 | 
						|
 | 
						|
static int
 | 
						|
normalize_posix_path (const char *src, char *dst, char *&tail)
 | 
						|
{
 | 
						|
  const char *in_src = src;
 | 
						|
  char *dst_start = dst;
 | 
						|
  syscall_printf ("src %s", src);
 | 
						|
 | 
						|
  if (isdrive (src) || *src == '\\')
 | 
						|
    goto win32_path;
 | 
						|
 | 
						|
  tail = dst;
 | 
						|
  if (!isslash (src[0]))
 | 
						|
    {
 | 
						|
      if (!cygheap->cwd.get (dst))
 | 
						|
	return get_errno ();
 | 
						|
      tail = strchr (tail, '\0');
 | 
						|
      if (isslash (dst[0]) && isslash (dst[1]))
 | 
						|
        ++dst_start;
 | 
						|
      if (*src == '.')
 | 
						|
	{
 | 
						|
	  if (tail == dst_start + 1 && *dst_start == '/')
 | 
						|
	     tail--;
 | 
						|
	  goto sawdot;
 | 
						|
	}
 | 
						|
      if (tail > dst && !isslash (tail[-1]))
 | 
						|
	*tail++ = '/';
 | 
						|
    }
 | 
						|
  /* Two leading /'s?  If so, preserve them.  */
 | 
						|
  else if (isslash (src[1]) && !isslash (src[2]))
 | 
						|
    {
 | 
						|
      *tail++ = *src++;
 | 
						|
      ++dst_start;
 | 
						|
    }
 | 
						|
 | 
						|
  while (*src)
 | 
						|
    {
 | 
						|
      if (*src == '\\')
 | 
						|
	goto win32_path;
 | 
						|
      /* Strip runs of /'s.  */
 | 
						|
      if (!isslash (*src))
 | 
						|
	*tail++ = *src++;
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  while (*++src)
 | 
						|
	    {
 | 
						|
	      if (isslash (*src))
 | 
						|
		continue;
 | 
						|
 | 
						|
	      if (*src != '.')
 | 
						|
		break;
 | 
						|
 | 
						|
	    sawdot:
 | 
						|
	      if (src[1] != '.')
 | 
						|
		{
 | 
						|
		  if (!src[1])
 | 
						|
		    {
 | 
						|
		      *tail++ = '/';
 | 
						|
		      goto done;
 | 
						|
		    }
 | 
						|
		  if (!isslash (src[1]))
 | 
						|
		    break;
 | 
						|
		}
 | 
						|
	      else if (src[2] && !isslash (src[2]))
 | 
						|
		break;
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  while (tail > dst_start && !isslash (*--tail))
 | 
						|
		    continue;
 | 
						|
		  src++;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
 | 
						|
	  *tail++ = '/';
 | 
						|
	}
 | 
						|
	if ((tail - dst) >= CYG_MAX_PATH)
 | 
						|
	  {
 | 
						|
	    debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src);
 | 
						|
	    return ENAMETOOLONG;
 | 
						|
	  }
 | 
						|
    }
 | 
						|
 | 
						|
done:
 | 
						|
  *tail = '\0';
 | 
						|
 | 
						|
  debug_printf ("%s = normalize_posix_path (%s)", dst, in_src);
 | 
						|
  return 0;
 | 
						|
 | 
						|
win32_path:
 | 
						|
  int err = normalize_win32_path (in_src, dst, tail);
 | 
						|
  if (!err)
 | 
						|
    for (char *p = dst; (p = strchr (p, '\\')); p++)
 | 
						|
      *p = '/';
 | 
						|
  return err;
 | 
						|
}
 | 
						|
 | 
						|
inline void
 | 
						|
path_conv::add_ext_from_sym (symlink_info &sym)
 | 
						|
{
 | 
						|
  if (sym.ext_here && *sym.ext_here)
 | 
						|
    {
 | 
						|
      known_suffix = path + sym.extn;
 | 
						|
      if (sym.ext_tacked_on)
 | 
						|
	strcpy (known_suffix, sym.ext_here);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void __stdcall mkrelpath (char *dst) __attribute__ ((regparm (2)));
 | 
						|
static void __stdcall
 | 
						|
mkrelpath (char *path)
 | 
						|
{
 | 
						|
  char cwd_win32[CYG_MAX_PATH];
 | 
						|
  if (!cygheap->cwd.get (cwd_win32, 0))
 | 
						|
    return;
 | 
						|
 | 
						|
  unsigned cwdlen = strlen (cwd_win32);
 | 
						|
  if (!path_prefix_p (cwd_win32, path, cwdlen))
 | 
						|
    return;
 | 
						|
 | 
						|
  size_t n = strlen (path);
 | 
						|
  if (n < cwdlen)
 | 
						|
    return;
 | 
						|
 | 
						|
  char *tail = path;
 | 
						|
  if (n == cwdlen)
 | 
						|
    tail += cwdlen;
 | 
						|
  else
 | 
						|
    tail += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1;
 | 
						|
 | 
						|
  memmove (path, tail, strlen (tail) + 1);
 | 
						|
  if (!*path)
 | 
						|
    strcpy (path, ".");
 | 
						|
}
 | 
						|
 | 
						|
#define MAX_FS_INFO_CNT 25
 | 
						|
fs_info fsinfo[MAX_FS_INFO_CNT];
 | 
						|
LONG fsinfo_cnt;
 | 
						|
 | 
						|
bool
 | 
						|
fs_info::update (const char *win32_path)
 | 
						|
{
 | 
						|
  char fsname [CYG_MAX_PATH];
 | 
						|
  char root_dir [CYG_MAX_PATH];
 | 
						|
  bool ret;
 | 
						|
 | 
						|
  if (!rootdir (win32_path, root_dir))
 | 
						|
    {
 | 
						|
      debug_printf ("Cannot get root component of path %s", win32_path);
 | 
						|
      clear ();
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
  __ino64_t tmp_name_hash = hash_path_name (1, root_dir);
 | 
						|
  if (tmp_name_hash == name_hash)
 | 
						|
    return true;
 | 
						|
  int idx = 0;
 | 
						|
  LONG cur_fsinfo_cnt = fsinfo_cnt;
 | 
						|
  while (idx < cur_fsinfo_cnt && fsinfo[idx].name_hash)
 | 
						|
    {
 | 
						|
      if (tmp_name_hash == fsinfo[idx].name_hash)
 | 
						|
	{
 | 
						|
	  *this = fsinfo[idx];
 | 
						|
	  return true;
 | 
						|
	}
 | 
						|
      ++idx;
 | 
						|
    }
 | 
						|
  name_hash = tmp_name_hash;
 | 
						|
 | 
						|
  /* I have no idea why, but some machines require SeChangeNotifyPrivilege
 | 
						|
     to access volume information. */
 | 
						|
  push_thread_privilege (SE_CHANGE_NOTIFY_PRIV, true);
 | 
						|
 | 
						|
  drive_type (GetDriveType (root_dir));
 | 
						|
  if (drive_type () == DRIVE_REMOTE
 | 
						|
      || (drive_type () == DRIVE_UNKNOWN
 | 
						|
	  && (root_dir[0] == '\\' && root_dir[1] == '\\')))
 | 
						|
    is_remote_drive (true);
 | 
						|
  else
 | 
						|
    is_remote_drive (false);
 | 
						|
 | 
						|
  ret = GetVolumeInformation (root_dir, NULL, 0, &status.serial, NULL,
 | 
						|
			      &status.flags, fsname, sizeof (fsname));
 | 
						|
 | 
						|
  pop_thread_privilege ();
 | 
						|
 | 
						|
  if (!ret && !is_remote_drive ())
 | 
						|
    {
 | 
						|
      debug_printf ("Cannot get volume information (%s), %E", root_dir);
 | 
						|
      has_buggy_open (false);
 | 
						|
      has_ea (false);
 | 
						|
      flags () = serial () = 0;
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
  /* FIXME: Samba by default returns "NTFS" in file system name, but
 | 
						|
   * doesn't support Extended Attributes. If there's some fast way to
 | 
						|
   * distinguish between samba and real ntfs, it should be implemented
 | 
						|
   * here.
 | 
						|
   */
 | 
						|
  has_ea (!is_remote_drive () && strcmp (fsname, "NTFS") == 0);
 | 
						|
  has_acls ((flags () & FS_PERSISTENT_ACLS)
 | 
						|
	    && (allow_smbntsec || !is_remote_drive ()));
 | 
						|
  is_fat (strncasematch (fsname, "FAT", 3));
 | 
						|
  /* Known file systems with buggy open calls. Further explanation
 | 
						|
     in fhandler.cc (fhandler_disk_file::open). */
 | 
						|
  has_buggy_open (!strcmp (fsname, "SUNWNFS"));
 | 
						|
 | 
						|
  /* Only append non-removable drives to the global fsinfo storage */
 | 
						|
  if (drive_type () != DRIVE_REMOVABLE && drive_type () != DRIVE_CDROM
 | 
						|
      && idx < MAX_FS_INFO_CNT)
 | 
						|
    {
 | 
						|
      LONG exc_cnt;
 | 
						|
      while ((exc_cnt = InterlockedExchange (&fsinfo_cnt, -1)) == -1)
 | 
						|
	low_priority_sleep (0);
 | 
						|
      if (exc_cnt < MAX_FS_INFO_CNT)
 | 
						|
	{
 | 
						|
	  /* Check if another thread has already appended that very drive */
 | 
						|
	  while (idx < exc_cnt)
 | 
						|
	    {
 | 
						|
	      if (fsinfo[idx++].name_hash == name_hash)
 | 
						|
		goto done;
 | 
						|
	    }
 | 
						|
	  fsinfo[exc_cnt++] = *this;
 | 
						|
	}
 | 
						|
     done:
 | 
						|
      InterlockedExchange (&fsinfo_cnt, exc_cnt);
 | 
						|
    }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
path_conv::fillin (HANDLE h)
 | 
						|
{
 | 
						|
  BY_HANDLE_FILE_INFORMATION local;
 | 
						|
  if (!GetFileInformationByHandle (h, &local))
 | 
						|
    {
 | 
						|
      fileattr = INVALID_FILE_ATTRIBUTES;
 | 
						|
      fs.serial () = 0;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      fileattr = local.dwFileAttributes;
 | 
						|
      fs.serial () = local.dwVolumeSerialNumber;
 | 
						|
    }
 | 
						|
    fs.drive_type (DRIVE_UNKNOWN);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
path_conv::set_normalized_path (const char *path_copy, bool strip_tail)
 | 
						|
{
 | 
						|
  char *p = strchr (path_copy, '\0');
 | 
						|
 | 
						|
  if (strip_tail)
 | 
						|
    {
 | 
						|
      while (*--p == '.' || *p == ' ')
 | 
						|
	continue;
 | 
						|
      *++p = '\0';
 | 
						|
    }
 | 
						|
 | 
						|
  size_t n = 1 + p - path_copy;
 | 
						|
 | 
						|
  normalized_path = path + sizeof (path) - n;
 | 
						|
 | 
						|
  char *eopath = strchr (path, '\0');
 | 
						|
  if (normalized_path > eopath)
 | 
						|
    normalized_path_size = n;
 | 
						|
  else
 | 
						|
    {
 | 
						|
      normalized_path = (char *) cmalloc (HEAP_STR, n);
 | 
						|
      normalized_path_size = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  memcpy (normalized_path, path_copy, n);
 | 
						|
}
 | 
						|
 | 
						|
PUNICODE_STRING
 | 
						|
path_conv::get_nt_native_path (UNICODE_STRING &upath)
 | 
						|
{
 | 
						|
  if (path[0] != '\\')			/* X:\...  or NUL, etc. */
 | 
						|
    {
 | 
						|
      str2uni_cat (upath, "\\??\\");
 | 
						|
      str2uni_cat (upath, path);
 | 
						|
    }
 | 
						|
  else if (path[1] != '\\')		/* \Device\... */
 | 
						|
    str2uni_cat (upath, path);
 | 
						|
  else if (path[2] != '.'
 | 
						|
	   || path[3] != '\\')		/* \\server\share\... */
 | 
						|
    {
 | 
						|
      str2uni_cat (upath, "\\??\\UNC\\");
 | 
						|
      str2uni_cat (upath, path + 2);
 | 
						|
    }
 | 
						|
  else					/* \\.\device */
 | 
						|
    {
 | 
						|
      str2uni_cat (upath, "\\??\\");
 | 
						|
      str2uni_cat (upath, path + 4);
 | 
						|
    }
 | 
						|
  return &upath;
 | 
						|
}
 | 
						|
 | 
						|
/* Convert an arbitrary path SRC to a pure Win32 path, suitable for
 | 
						|
   passing to Win32 API routines.
 | 
						|
 | 
						|
   If an error occurs, `error' is set to the errno value.
 | 
						|
   Otherwise it is set to 0.
 | 
						|
 | 
						|
   follow_mode values:
 | 
						|
	SYMLINK_FOLLOW	    - convert to PATH symlink points to
 | 
						|
	SYMLINK_NOFOLLOW    - convert to PATH of symlink itself
 | 
						|
	SYMLINK_IGNORE	    - do not check PATH for symlinks
 | 
						|
	SYMLINK_CONTENTS    - just return symlink contents
 | 
						|
*/
 | 
						|
 | 
						|
void
 | 
						|
path_conv::check (const char *src, unsigned opt,
 | 
						|
		  const suffix_info *suffixes)
 | 
						|
{
 | 
						|
  /* This array is used when expanding symlinks.  It is CYG_MAX_PATH * 2
 | 
						|
     in length so that we can hold the expanded symlink plus a
 | 
						|
     trailer.  */
 | 
						|
  char path_copy[CYG_MAX_PATH + 3];
 | 
						|
  char tmp_buf[2 * CYG_MAX_PATH + 3];
 | 
						|
  symlink_info sym;
 | 
						|
  bool need_directory = 0;
 | 
						|
  bool saw_symlinks = 0;
 | 
						|
  bool is_relpath;
 | 
						|
  char *tail, *path_end;
 | 
						|
 | 
						|
#if 0
 | 
						|
  static path_conv last_path_conv;
 | 
						|
  static char last_src[CYG_MAX_PATH];
 | 
						|
 | 
						|
  if (*last_src && strcmp (last_src, src) == 0)
 | 
						|
    {
 | 
						|
      *this = last_path_conv;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted ())
 | 
						|
    {
 | 
						|
      error = EFAULT;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  int loop = 0;
 | 
						|
  path_flags = 0;
 | 
						|
  known_suffix = NULL;
 | 
						|
  fileattr = INVALID_FILE_ATTRIBUTES;
 | 
						|
  case_clash = false;
 | 
						|
  memset (&dev, 0, sizeof (dev));
 | 
						|
  fs.clear ();
 | 
						|
  normalized_path = NULL;
 | 
						|
  int component = 0;		// Number of translated components
 | 
						|
 | 
						|
  if (!(opt & PC_NULLEMPTY))
 | 
						|
    error = 0;
 | 
						|
  else if (!*src)
 | 
						|
    {
 | 
						|
      error = ENOENT;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  /* This loop handles symlink expansion.  */
 | 
						|
  for (;;)
 | 
						|
    {
 | 
						|
      MALLOC_CHECK;
 | 
						|
      assert (src);
 | 
						|
 | 
						|
      is_relpath = !isabspath (src);
 | 
						|
      error = normalize_posix_path (src, path_copy, tail);
 | 
						|
      if (error)
 | 
						|
	return;
 | 
						|
 | 
						|
      /* Detect if the user was looking for a directory.  We have to strip the
 | 
						|
	 trailing slash initially while trying to add extensions but take it
 | 
						|
	 into account during processing */
 | 
						|
      if (tail > path_copy + 2 && isslash (tail[-1]))
 | 
						|
	{
 | 
						|
	  need_directory = 1;
 | 
						|
	  *--tail = '\0';
 | 
						|
	}
 | 
						|
      path_end = tail;
 | 
						|
 | 
						|
      /* Scan path_copy from right to left looking either for a symlink
 | 
						|
	 or an actual existing file.  If an existing file is found, just
 | 
						|
	 return.  If a symlink is found, exit the for loop.
 | 
						|
	 Also: be careful to preserve the errno returned from
 | 
						|
	 symlink.check as the caller may need it. */
 | 
						|
      /* FIXME: Do we have to worry about multiple \'s here? */
 | 
						|
      component = 0;		// Number of translated components
 | 
						|
      sym.contents[0] = '\0';
 | 
						|
 | 
						|
      int symlen = 0;
 | 
						|
 | 
						|
      for (unsigned pflags_or = opt & PC_NO_ACCESS_CHECK; ; pflags_or = 0)
 | 
						|
	{
 | 
						|
	  const suffix_info *suff;
 | 
						|
	  char pathbuf[CYG_MAX_PATH];
 | 
						|
	  char *full_path;
 | 
						|
 | 
						|
	  /* Don't allow symlink.check to set anything in the path_conv
 | 
						|
	     class if we're working on an inner component of the path */
 | 
						|
	  if (component)
 | 
						|
	    {
 | 
						|
	      suff = NULL;
 | 
						|
	      sym.pflags = 0;
 | 
						|
	      full_path = pathbuf;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      suff = suffixes;
 | 
						|
	      sym.pflags = path_flags;
 | 
						|
	      full_path = this->path;
 | 
						|
	    }
 | 
						|
 | 
						|
	  /* Convert to native path spec sans symbolic link info. */
 | 
						|
	  error = mount_table->conv_to_win32_path (path_copy, full_path, dev,
 | 
						|
						   &sym.pflags);
 | 
						|
 | 
						|
	  if (error)
 | 
						|
	    return;
 | 
						|
 | 
						|
	  sym.pflags |= pflags_or;
 | 
						|
 | 
						|
	  if (dev.major == DEV_CYGDRIVE_MAJOR)
 | 
						|
	    {
 | 
						|
	      if (!component)
 | 
						|
		fileattr = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY;
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  fileattr = GetFileAttributes (this->path);
 | 
						|
		  dev.devn = FH_FS;
 | 
						|
		}
 | 
						|
	      goto out;
 | 
						|
	    }
 | 
						|
	  else if (dev == FH_DEV)
 | 
						|
	    {
 | 
						|
	      dev.devn = FH_FS;
 | 
						|
#if 0
 | 
						|
	      fileattr = GetFileAttributes (this->path);
 | 
						|
	      if (!component && fileattr == INVALID_FILE_ATTRIBUTES)
 | 
						|
		{
 | 
						|
		  fileattr = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY;
 | 
						|
		  goto out;
 | 
						|
		}
 | 
						|
#endif
 | 
						|
	    }
 | 
						|
	  else if (isvirtual_dev (dev.devn))
 | 
						|
	    {
 | 
						|
	      /* FIXME: Calling build_fhandler here is not the right way to handle this. */
 | 
						|
	      fhandler_virtual *fh = (fhandler_virtual *) build_fh_dev (dev, path_copy);
 | 
						|
	      int file_type = fh->exists ();
 | 
						|
	      if (file_type == -2)
 | 
						|
		{
 | 
						|
		  fh->fill_filebuf ();
 | 
						|
		  symlen = sym.set (fh->get_filebuf ());
 | 
						|
		}
 | 
						|
	      delete fh;
 | 
						|
	      switch (file_type)
 | 
						|
		{
 | 
						|
		  case 1:
 | 
						|
		  case 2:
 | 
						|
		    if (component == 0)
 | 
						|
		      fileattr = FILE_ATTRIBUTE_DIRECTORY;
 | 
						|
		    break;
 | 
						|
		  case -1:
 | 
						|
		    if (component == 0)
 | 
						|
		      fileattr = 0;
 | 
						|
		    break;
 | 
						|
		  case -2:	/* /proc/self or /proc/<pid>/symlinks */
 | 
						|
		    goto is_virtual_symlink;
 | 
						|
		  case -3:	/* /proc/<pid>/fd/pipe:[] */
 | 
						|
		    if (component == 0)
 | 
						|
		      {
 | 
						|
			fileattr = 0;
 | 
						|
			dev.parse (FH_PIPE);
 | 
						|
		      }
 | 
						|
		    break;
 | 
						|
		  case -4:	/* /proc/<pid>/fd/socket:[] */
 | 
						|
		    if (component == 0)
 | 
						|
		      {
 | 
						|
			fileattr = 0;
 | 
						|
			dev.parse (FH_TCP);
 | 
						|
		      }
 | 
						|
		    break;
 | 
						|
		  default:
 | 
						|
		    if (component == 0)
 | 
						|
		      fileattr = INVALID_FILE_ATTRIBUTES;
 | 
						|
		    goto virtual_component_retry;
 | 
						|
		}
 | 
						|
	      if (component == 0 || dev.devn != FH_NETDRIVE)
 | 
						|
		path_flags |= PATH_RO;
 | 
						|
	      goto out;
 | 
						|
	    }
 | 
						|
	  /* devn should not be a device.  If it is, then stop parsing now. */
 | 
						|
	  else if (dev.devn != FH_FS)
 | 
						|
	    {
 | 
						|
	      fileattr = 0;
 | 
						|
	      path_flags = sym.pflags;
 | 
						|
	      if (component)
 | 
						|
		{
 | 
						|
		  error = ENOTDIR;
 | 
						|
		  return;
 | 
						|
		}
 | 
						|
	      goto out;		/* Found a device.  Stop parsing. */
 | 
						|
	    }
 | 
						|
 | 
						|
	  /* If path is only a drivename, Windows interprets it as the
 | 
						|
	     current working directory on this drive instead of the root
 | 
						|
	     dir which is what we want. So we need the trailing backslash
 | 
						|
	     in this case. */
 | 
						|
	  if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0')
 | 
						|
	    {
 | 
						|
	      full_path[2] = '\\';
 | 
						|
	      full_path[3] = '\0';
 | 
						|
	    }
 | 
						|
 | 
						|
	  symlen = sym.check (full_path, suff, opt | fs.has_ea ());
 | 
						|
 | 
						|
is_virtual_symlink:
 | 
						|
 | 
						|
	  if (sym.isdevice)
 | 
						|
	    {
 | 
						|
	      dev.parse (sym.major, sym.minor);
 | 
						|
	      dev.setfs (1);
 | 
						|
	      dev.mode = sym.mode;
 | 
						|
	      fileattr = sym.fileattr;
 | 
						|
	      goto out;
 | 
						|
	    }
 | 
						|
 | 
						|
	  if (sym.pflags & PATH_SOCKET)
 | 
						|
	    {
 | 
						|
	      if (component)
 | 
						|
		{
 | 
						|
		  error = ENOTDIR;
 | 
						|
		  return;
 | 
						|
		}
 | 
						|
	      fileattr = sym.fileattr;
 | 
						|
	      dev.parse (FH_UNIX);
 | 
						|
	      dev.setfs (1);
 | 
						|
	      goto out;
 | 
						|
	    }
 | 
						|
 | 
						|
	  if (sym.case_clash)
 | 
						|
	    {
 | 
						|
	      if (pcheck_case == PCHECK_STRICT)
 | 
						|
		{
 | 
						|
		  case_clash = true;
 | 
						|
		  error = ENOENT;
 | 
						|
		  goto out;
 | 
						|
		}
 | 
						|
	      /* If pcheck_case==PCHECK_ADJUST the case_clash is remembered
 | 
						|
		 if the last component is concerned. This allows functions
 | 
						|
		 which shall create files to avoid overriding already existing
 | 
						|
		 files with another case. */
 | 
						|
	      if (!component)
 | 
						|
		case_clash = true;
 | 
						|
	    }
 | 
						|
	  if (!(opt & PC_SYM_IGNORE))
 | 
						|
	    {
 | 
						|
	      if (!component)
 | 
						|
		{
 | 
						|
		  fileattr = sym.fileattr;
 | 
						|
		  path_flags = sym.pflags;
 | 
						|
		}
 | 
						|
 | 
						|
	      /* If symlink.check found an existing non-symlink file, then
 | 
						|
		 it sets the appropriate flag.  It also sets any suffix found
 | 
						|
		 into `ext_here'. */
 | 
						|
	      if (!sym.issymlink && sym.fileattr != INVALID_FILE_ATTRIBUTES)
 | 
						|
		{
 | 
						|
		  error = sym.error;
 | 
						|
		  if (component == 0)
 | 
						|
		    add_ext_from_sym (sym);
 | 
						|
		  else if (!(sym.fileattr & FILE_ATTRIBUTE_DIRECTORY))
 | 
						|
		    {
 | 
						|
		      error = ENOTDIR;
 | 
						|
		      goto out;
 | 
						|
		    }
 | 
						|
		  if (pcheck_case == PCHECK_RELAXED)
 | 
						|
		    goto out;	// file found
 | 
						|
		  /* Avoid further symlink evaluation. Only case checks are
 | 
						|
		     done now. */
 | 
						|
		  opt |= PC_SYM_IGNORE;
 | 
						|
		}
 | 
						|
	      /* Found a symlink if symlen > 0.  If component == 0, then the
 | 
						|
		 src path itself was a symlink.  If !follow_mode then
 | 
						|
		 we're done.  Otherwise we have to insert the path found
 | 
						|
		 into the full path that we are building and perform all of
 | 
						|
		 these operations again on the newly derived path. */
 | 
						|
	      else if (symlen > 0)
 | 
						|
		{
 | 
						|
		  saw_symlinks = 1;
 | 
						|
		  if (component == 0 && !need_directory && !(opt & PC_SYM_FOLLOW))
 | 
						|
		    {
 | 
						|
		      set_symlink (symlen); // last component of path is a symlink.
 | 
						|
		      if (opt & PC_SYM_CONTENTS)
 | 
						|
			{
 | 
						|
			  strcpy (path, sym.contents);
 | 
						|
			  goto out;
 | 
						|
			}
 | 
						|
		      add_ext_from_sym (sym);
 | 
						|
		      if (pcheck_case == PCHECK_RELAXED)
 | 
						|
			goto out;
 | 
						|
		      /* Avoid further symlink evaluation. Only case checks are
 | 
						|
			 done now. */
 | 
						|
		      opt |= PC_SYM_IGNORE;
 | 
						|
		    }
 | 
						|
		  else
 | 
						|
		    break;
 | 
						|
		}
 | 
						|
	      else if (sym.error && sym.error != ENOENT && sym.error != ENOSHARE)
 | 
						|
		{
 | 
						|
		  error = sym.error;
 | 
						|
		  goto out;
 | 
						|
		}
 | 
						|
	      /* No existing file found. */
 | 
						|
	    }
 | 
						|
 | 
						|
virtual_component_retry:
 | 
						|
	  /* Find the new "tail" of the path, e.g. in '/for/bar/baz',
 | 
						|
	     /baz is the tail. */
 | 
						|
	  if (tail != path_end)
 | 
						|
	    *tail = '/';
 | 
						|
	  while (--tail > path_copy + 1 && *tail != '/') {}
 | 
						|
	  /* Exit loop if there is no tail or we are at the
 | 
						|
	     beginning of a UNC path */
 | 
						|
	  if (tail <= path_copy + 1)
 | 
						|
	    goto out;	// all done
 | 
						|
 | 
						|
	  /* Haven't found an existing pathname component yet.
 | 
						|
	     Pinch off the tail and try again. */
 | 
						|
	  *tail = '\0';
 | 
						|
	  component++;
 | 
						|
	}
 | 
						|
 | 
						|
      /* Arrive here if above loop detected a symlink. */
 | 
						|
      if (++loop > MAX_LINK_DEPTH)
 | 
						|
	{
 | 
						|
	  error = ELOOP;   // Eep.
 | 
						|
	  return;
 | 
						|
	}
 | 
						|
 | 
						|
      MALLOC_CHECK;
 | 
						|
 | 
						|
 | 
						|
      /* Place the link content, possibly with head and/or tail, in tmp_buf */
 | 
						|
 | 
						|
      char *headptr;
 | 
						|
      if (isabspath (sym.contents))
 | 
						|
	headptr = tmp_buf;	/* absolute path */
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* Copy the first part of the path (with ending /) and point to the end. */
 | 
						|
	  char *prevtail = tail;
 | 
						|
	  while (--prevtail > path_copy  && *prevtail != '/') {}
 | 
						|
	  int headlen = prevtail - path_copy + 1;;
 | 
						|
	  memcpy (tmp_buf, path_copy, headlen);
 | 
						|
	  headptr = &tmp_buf[headlen];
 | 
						|
	}
 | 
						|
 | 
						|
      /* Make sure there is enough space */
 | 
						|
      if (headptr + symlen >= tmp_buf + sizeof (tmp_buf))
 | 
						|
	{
 | 
						|
	too_long:
 | 
						|
	  error = ENAMETOOLONG;
 | 
						|
	  strcpy (path, "::ENAMETOOLONG::");
 | 
						|
	  return;
 | 
						|
	}
 | 
						|
 | 
						|
     /* Copy the symlink contents to the end of tmp_buf.
 | 
						|
	Convert slashes. */
 | 
						|
      for (char *p = sym.contents; *p; p++)
 | 
						|
	*headptr++ = *p == '\\' ? '/' : *p;
 | 
						|
      *headptr = '\0';
 | 
						|
 | 
						|
      /* Copy any tail component (with the 0) */
 | 
						|
      if (tail++ < path_end)
 | 
						|
	{
 | 
						|
	  /* Add a slash if needed. There is space. */
 | 
						|
	  if (*(headptr - 1) != '/')
 | 
						|
	    *headptr++ = '/';
 | 
						|
	  int taillen = path_end - tail + 1;
 | 
						|
	  if (headptr + taillen > tmp_buf + sizeof (tmp_buf))
 | 
						|
	    goto too_long;
 | 
						|
	  memcpy (headptr, tail, taillen);
 | 
						|
	}
 | 
						|
 | 
						|
      /* Evaluate everything all over again. */
 | 
						|
      src = tmp_buf;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!(opt & PC_SYM_CONTENTS))
 | 
						|
    add_ext_from_sym (sym);
 | 
						|
 | 
						|
out:
 | 
						|
  bool strip_tail = false;
 | 
						|
  if (dev.devn == FH_NETDRIVE && component)
 | 
						|
    {
 | 
						|
      /* This case indicates a non-existant resp. a non-retrievable
 | 
						|
	 share.  This happens for instance if the share is a printer.
 | 
						|
	 In this case the path must not be treated like a FH_NETDRIVE,
 | 
						|
	 but like a FH_FS instead, so the usual open call for files
 | 
						|
	 is used on it. */
 | 
						|
      dev.parse (FH_FS);
 | 
						|
    }
 | 
						|
  else if (isvirtual_dev (dev.devn) && fileattr == INVALID_FILE_ATTRIBUTES)
 | 
						|
    {
 | 
						|
      error = dev.devn == FH_NETDRIVE ? ENOSHARE : ENOENT;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  else if (!need_directory || error)
 | 
						|
    /* nothing to do */;
 | 
						|
  else if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
 | 
						|
    path_flags &= ~PATH_SYMLINK;
 | 
						|
  else
 | 
						|
    {
 | 
						|
      debug_printf ("%s is a non-directory", path);
 | 
						|
      error = ENOTDIR;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  if (dev.isfs ())
 | 
						|
    {
 | 
						|
      if (strncmp (path, "\\\\.\\", 4))
 | 
						|
	{
 | 
						|
	  /* Windows ignores trailing dots and spaces */
 | 
						|
	  char *tail = NULL;
 | 
						|
	  for (char *p = path; *p; p++)
 | 
						|
	    if (*p != '.' && *p != ' ')
 | 
						|
	      tail = NULL;
 | 
						|
	    else if (p[1] == '\\')
 | 
						|
	      {
 | 
						|
		memmove (p, p + 1, strlen (p));
 | 
						|
		tail = NULL;
 | 
						|
	      }
 | 
						|
	    else if (!tail)
 | 
						|
	      tail = p;
 | 
						|
 | 
						|
	  if (!tail || tail == path)
 | 
						|
	    /* nothing */;
 | 
						|
	  else if (tail[-1] != '\\')
 | 
						|
	    {
 | 
						|
	      *tail = '\0';
 | 
						|
	      strip_tail = true;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      error = ENOENT;
 | 
						|
	      return;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
      if (fs.update (path))
 | 
						|
	{
 | 
						|
	  debug_printf ("this->path(%s), has_acls(%d)", path, fs.has_acls ());
 | 
						|
	  if (fs.has_acls () && allow_ntsec)
 | 
						|
	    set_exec (0);  /* We really don't know if this is executable or not here
 | 
						|
			      but set it to not executable since it will be figured out
 | 
						|
			      later by anything which cares about this. */
 | 
						|
	}
 | 
						|
      if (exec_state () != dont_know_if_executable)
 | 
						|
	/* ok */;
 | 
						|
      else if (isdir ())
 | 
						|
	set_exec (1);
 | 
						|
      else if (issymlink () || issocket ())
 | 
						|
	set_exec (0);
 | 
						|
    }
 | 
						|
 | 
						|
#if 0
 | 
						|
  if (issocket ())
 | 
						|
    devn = FH_SOCKET;
 | 
						|
#endif
 | 
						|
 | 
						|
  if (opt & PC_NOFULL)
 | 
						|
    {
 | 
						|
      if (is_relpath)
 | 
						|
	mkrelpath (this->path);
 | 
						|
      if (need_directory)
 | 
						|
	{
 | 
						|
	  size_t n = strlen (this->path);
 | 
						|
	  /* Do not add trailing \ to UNC device names like \\.\a: */
 | 
						|
	  if (this->path[n - 1] != '\\' &&
 | 
						|
	      (strncmp (this->path, "\\\\.\\", 4) != 0))
 | 
						|
	    {
 | 
						|
	      this->path[n] = '\\';
 | 
						|
	      this->path[n + 1] = '\0';
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (saw_symlinks)
 | 
						|
    set_has_symlinks ();
 | 
						|
 | 
						|
  if (!error && !isdir () && !(path_flags & PATH_ALL_EXEC))
 | 
						|
    {
 | 
						|
      const char *p = strchr (path, '\0') - 4;
 | 
						|
      if (p >= path &&
 | 
						|
	  (strcasematch (".exe", p) ||
 | 
						|
	   strcasematch (".bat", p) ||
 | 
						|
	   strcasematch (".com", p)))
 | 
						|
	path_flags |= PATH_EXEC;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!(opt & PC_POSIX))
 | 
						|
    normalized_path_size = 0;
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if (tail < path_end && tail > path_copy + 1)
 | 
						|
	*tail = '/';
 | 
						|
      set_normalized_path (path_copy, strip_tail);
 | 
						|
    }
 | 
						|
 | 
						|
#if 0
 | 
						|
  if (!error)
 | 
						|
    {
 | 
						|
      last_path_conv = *this;
 | 
						|
      strcpy (last_src, src);
 | 
						|
    }
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
path_conv::set_name (const char *win32, const char *posix)
 | 
						|
{
 | 
						|
  if (!normalized_path_size && normalized_path)
 | 
						|
    cfree (normalized_path);
 | 
						|
  strcpy (path, win32);
 | 
						|
  set_normalized_path (posix, false);
 | 
						|
}
 | 
						|
 | 
						|
path_conv::~path_conv ()
 | 
						|
{
 | 
						|
  if (!normalized_path_size && normalized_path)
 | 
						|
    {
 | 
						|
      cfree (normalized_path);
 | 
						|
      normalized_path = NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* Return true if src_path is a valid, internally supported device name.
 | 
						|
   In that case, win32_path gets the corresponding NT device name and
 | 
						|
   dev is appropriately filled with device information. */
 | 
						|
 | 
						|
static bool
 | 
						|
win32_device_name (const char *src_path, char *win32_path, device& dev)
 | 
						|
{
 | 
						|
  dev.parse (src_path);
 | 
						|
  if (dev == FH_FS || dev == FH_DEV)
 | 
						|
    return false;
 | 
						|
  strcpy (win32_path, dev.native);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/* is_unc_share: Return non-zero if PATH begins with //UNC/SHARE */
 | 
						|
 | 
						|
static bool __stdcall
 | 
						|
is_unc_share (const char *path)
 | 
						|
{
 | 
						|
  const char *p;
 | 
						|
  return (isdirsep (path[0])
 | 
						|
	 && isdirsep (path[1])
 | 
						|
	 && (isalnum (path[2]) || path[2] == '.')
 | 
						|
	 && ((p = strpbrk (path + 3, "\\/")) != NULL)
 | 
						|
	 && isalnum (p[1]));
 | 
						|
}
 | 
						|
 | 
						|
/* Normalize a Win32 path.
 | 
						|
   /'s are converted to \'s in the process.
 | 
						|
   All duplicate \'s, except for 2 leading \'s, are deleted.
 | 
						|
 | 
						|
   The result is 0 for success, or an errno error value.
 | 
						|
   FIXME: A lot of this should be mergeable with the POSIX critter.  */
 | 
						|
static int
 | 
						|
normalize_win32_path (const char *src, char *dst, char *&tail)
 | 
						|
{
 | 
						|
  const char *src_start = src;
 | 
						|
  bool beg_src_slash = isdirsep (src[0]);
 | 
						|
 | 
						|
  tail = dst;
 | 
						|
  if (beg_src_slash && isdirsep (src[1]))
 | 
						|
    {
 | 
						|
      if (isdirsep (src[2]))
 | 
						|
        {
 | 
						|
	  /* More than two slashes are just folded into one. */
 | 
						|
	  src += 2;
 | 
						|
	  while (isdirsep (src[1]))
 | 
						|
	    ++src;
 | 
						|
	}
 | 
						|
      else
 | 
						|
        {
 | 
						|
	  /* Two slashes start a network or device path. */
 | 
						|
	  *tail++ = '\\';
 | 
						|
	  src++;
 | 
						|
	  if (src[1] == '.' && isdirsep (src[2]))
 | 
						|
	    {
 | 
						|
	      *tail++ = '\\';
 | 
						|
	      *tail++ = '.';
 | 
						|
	      src += 2;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  if (tail == dst && !isdrive (src) && *src != '/')
 | 
						|
    {
 | 
						|
      if (beg_src_slash)
 | 
						|
	tail += cygheap->cwd.get_drive (dst);
 | 
						|
      else if (!cygheap->cwd.get (dst, 0))
 | 
						|
	return get_errno ();
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  tail = strchr (tail, '\0');
 | 
						|
	  *tail++ = '\\';
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  while (*src)
 | 
						|
    {
 | 
						|
      /* Strip duplicate /'s.  */
 | 
						|
      if (isdirsep (src[0]) && isdirsep (src[1]))
 | 
						|
	src++;
 | 
						|
      /* Ignore "./".  */
 | 
						|
      else if (src[0] == '.' && isdirsep (src[1])
 | 
						|
	       && (src == src_start || isdirsep (src[-1])))
 | 
						|
	src += 2;
 | 
						|
 | 
						|
      /* Backup if "..".  */
 | 
						|
      else if (src[0] == '.' && src[1] == '.'
 | 
						|
	       /* dst must be greater than dst_start */
 | 
						|
	       && tail[-1] == '\\')
 | 
						|
	{
 | 
						|
	  if (!isdirsep (src[2]) && src[2] != '\0')
 | 
						|
	      *tail++ = *src++;
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      /* Back up over /, but not if it's the first one.  */
 | 
						|
	      if (tail > dst + 1)
 | 
						|
		tail--;
 | 
						|
	      /* Now back up to the next /.  */
 | 
						|
	      while (tail > dst + 1 && tail[-1] != '\\' && tail[-2] != ':')
 | 
						|
		tail--;
 | 
						|
	      src += 2;
 | 
						|
	      if (isdirsep (*src))
 | 
						|
		src++;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      /* Otherwise, add char to result.  */
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  if (*src == '/')
 | 
						|
	    *tail++ = '\\';
 | 
						|
	  else
 | 
						|
	    *tail++ = *src;
 | 
						|
	  src++;
 | 
						|
	}
 | 
						|
      if ((tail - dst) >= CYG_MAX_PATH)
 | 
						|
	return ENAMETOOLONG;
 | 
						|
    }
 | 
						|
   if (tail > dst + 1 && tail[-1] == '.' && tail[-2] == '\\')
 | 
						|
     tail--;
 | 
						|
  *tail = '\0';
 | 
						|
  debug_printf ("%s = normalize_win32_path (%s)", dst, src_start);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Various utilities.  */
 | 
						|
 | 
						|
/* slashify: Convert all back slashes in src path to forward slashes
 | 
						|
   in dst path.  Add a trailing slash to dst when trailing_slash_p arg
 | 
						|
   is set to 1. */
 | 
						|
 | 
						|
static void
 | 
						|
slashify (const char *src, char *dst, int trailing_slash_p)
 | 
						|
{
 | 
						|
  const char *start = src;
 | 
						|
 | 
						|
  while (*src)
 | 
						|
    {
 | 
						|
      if (*src == '\\')
 | 
						|
	*dst++ = '/';
 | 
						|
      else
 | 
						|
	*dst++ = *src;
 | 
						|
      ++src;
 | 
						|
    }
 | 
						|
  if (trailing_slash_p
 | 
						|
      && src > start
 | 
						|
      && !isdirsep (src[-1]))
 | 
						|
    *dst++ = '/';
 | 
						|
  *dst++ = 0;
 | 
						|
}
 | 
						|
 | 
						|
/* backslashify: Convert all forward slashes in src path to back slashes
 | 
						|
   in dst path.  Add a trailing slash to dst when trailing_slash_p arg
 | 
						|
   is set to 1. */
 | 
						|
 | 
						|
static void
 | 
						|
backslashify (const char *src, char *dst, int trailing_slash_p)
 | 
						|
{
 | 
						|
  const char *start = src;
 | 
						|
 | 
						|
  while (*src)
 | 
						|
    {
 | 
						|
      if (*src == '/')
 | 
						|
	*dst++ = '\\';
 | 
						|
      else
 | 
						|
	*dst++ = *src;
 | 
						|
      ++src;
 | 
						|
    }
 | 
						|
  if (trailing_slash_p
 | 
						|
      && src > start
 | 
						|
      && !isdirsep (src[-1]))
 | 
						|
    *dst++ = '\\';
 | 
						|
  *dst++ = 0;
 | 
						|
}
 | 
						|
 | 
						|
/* nofinalslash: Remove trailing / and \ from SRC (except for the
 | 
						|
   first one).  It is ok for src == dst.  */
 | 
						|
 | 
						|
void __stdcall
 | 
						|
nofinalslash (const char *src, char *dst)
 | 
						|
{
 | 
						|
  int len = strlen (src);
 | 
						|
  if (src != dst)
 | 
						|
    memcpy (dst, src, len + 1);
 | 
						|
  while (len > 1 && isdirsep (dst[--len]))
 | 
						|
    dst[len] = '\0';
 | 
						|
}
 | 
						|
 | 
						|
/* conv_path_list: Convert a list of path names to/from Win32/POSIX. */
 | 
						|
 | 
						|
static int
 | 
						|
conv_path_list (const char *src, char *dst, int to_posix)
 | 
						|
{
 | 
						|
  char *s;
 | 
						|
  char *d = dst;
 | 
						|
  char src_delim = to_posix ? ';' : ':';
 | 
						|
  char dst_delim = to_posix ? ':' : ';';
 | 
						|
  int (*conv_fn) (const char *, char *) = (to_posix
 | 
						|
					   ? cygwin_conv_to_posix_path
 | 
						|
					   : cygwin_conv_to_win32_path);
 | 
						|
 | 
						|
  char *srcbuf = (char *) alloca (strlen (src) + 1);
 | 
						|
 | 
						|
  for (;;)
 | 
						|
    {
 | 
						|
      s = strccpy (srcbuf, &src, src_delim);
 | 
						|
      int len = s - srcbuf;
 | 
						|
      if (len >= CYG_MAX_PATH)
 | 
						|
	return ENAMETOOLONG;
 | 
						|
      int err = (*conv_fn) (len ? srcbuf : ".", d);
 | 
						|
      if (err)
 | 
						|
	return err;
 | 
						|
      if (!*src++)
 | 
						|
	break;
 | 
						|
      d = strchr (d, '\0');
 | 
						|
      *d++ = dst_delim;
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* init: Initialize the mount table.  */
 | 
						|
 | 
						|
void
 | 
						|
mount_info::init ()
 | 
						|
{
 | 
						|
  nmounts = 0;
 | 
						|
 | 
						|
  /* Fetch the mount table and cygdrive-related information from
 | 
						|
     the registry.  */
 | 
						|
  from_registry ();
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
set_flags (unsigned *flags, unsigned val)
 | 
						|
{
 | 
						|
  *flags = val;
 | 
						|
  if (!(*flags & PATH_BINARY))
 | 
						|
    {
 | 
						|
      *flags |= PATH_TEXT;
 | 
						|
      debug_printf ("flags: text (%p)", *flags & (PATH_TEXT | PATH_BINARY));
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      *flags |= PATH_BINARY;
 | 
						|
      debug_printf ("flags: binary (%p)", *flags & (PATH_TEXT | PATH_BINARY));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static char dot_special_chars[] =
 | 
						|
    "."
 | 
						|
    "\001" "\002" "\003" "\004" "\005" "\006" "\007" "\010"
 | 
						|
    "\011" "\012" "\013" "\014" "\015" "\016" "\017" "\020"
 | 
						|
    "\021" "\022" "\023" "\024" "\025" "\026" "\027" "\030"
 | 
						|
    "\031" "\032" "\033" "\034" "\035" "\036" "\037"
 | 
						|
    ":"    "\\"   "*"    "?"    "%"
 | 
						|
    "A"    "B"    "C"    "D"    "E"    "F"    "G"    "H"
 | 
						|
    "I"    "J"    "K"    "L"    "M"    "N"    "O"    "P"
 | 
						|
    "Q"    "R"    "S"    "T"    "U"    "V"    "W"    "X"
 | 
						|
    "Y"    "Z";
 | 
						|
static char *special_chars = dot_special_chars + 1;
 | 
						|
static char special_introducers[] =
 | 
						|
    "anpcl";
 | 
						|
 | 
						|
static char
 | 
						|
special_char (const char *s, const char *valid_chars = special_chars)
 | 
						|
{
 | 
						|
  if (*s != '%' || strlen (s) < 3)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  char *p;
 | 
						|
  char hex[] = {s[1], s[2], '\0'};
 | 
						|
  unsigned char c = strtoul (hex, &p, 16);
 | 
						|
  p = strechr (valid_chars, c);
 | 
						|
  return *p;
 | 
						|
}
 | 
						|
 | 
						|
/* Determines if name is "special".  Assumes that name is empty or "absolute" */
 | 
						|
static int
 | 
						|
special_name (const char *s, int inc = 1)
 | 
						|
{
 | 
						|
  if (!*s)
 | 
						|
    return false;
 | 
						|
 | 
						|
  s += inc;
 | 
						|
 | 
						|
  if (strcmp (s, ".") == 0 || strcmp (s, "..") == 0)
 | 
						|
    return false;
 | 
						|
 | 
						|
  int n;
 | 
						|
  const char *p = NULL;
 | 
						|
  if (strncasematch (s, "conin$", n = 5)
 | 
						|
      || strncasematch (s, "conout$", n = 7)
 | 
						|
      || strncasematch (s, "nul", n = 3)
 | 
						|
      || strncasematch (s, "aux", 3)
 | 
						|
      || strncasematch (s, "prn", 3)
 | 
						|
      || strncasematch (s, "con", 3))
 | 
						|
    p = s + n;
 | 
						|
  else if (strncasematch (s, "com", 3) || strncasematch (s, "lpt", 3))
 | 
						|
    strtoul (s + 3, (char **) &p, 10);
 | 
						|
  if (p && (*p == '\0' || *p == '.'))
 | 
						|
    return -1;
 | 
						|
 | 
						|
  return (strchr (s, '\0')[-1] == '.')
 | 
						|
	 || (strpbrk (s, special_chars) && !strncasematch (s, "%2f", 3));
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
fnunmunge (char *dst, const char *src)
 | 
						|
{
 | 
						|
  bool converted = false;
 | 
						|
  char c;
 | 
						|
 | 
						|
  if ((c = special_char (src, special_introducers)))
 | 
						|
    {
 | 
						|
      __small_sprintf (dst, "%c%s", c, src + 3);
 | 
						|
      if (special_name (dst, 0))
 | 
						|
	{
 | 
						|
	  *dst++ = c;
 | 
						|
	  src += 3;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  while (*src)
 | 
						|
    if (!(c = special_char (src, dot_special_chars)))
 | 
						|
      *dst++ = *src++;
 | 
						|
    else
 | 
						|
      {
 | 
						|
	converted = true;
 | 
						|
	*dst++ = c;
 | 
						|
	src += 3;
 | 
						|
      }
 | 
						|
 | 
						|
  *dst = *src;
 | 
						|
  return converted;
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
copy1 (char *&d, const char *&src, int& left)
 | 
						|
{
 | 
						|
  left--;
 | 
						|
  if (left || !*src)
 | 
						|
    *d++ = *src++;
 | 
						|
  else
 | 
						|
    return true;
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
copyenc (char *&d, const char *&src, int& left)
 | 
						|
{
 | 
						|
  char buf[16];
 | 
						|
  int n = __small_sprintf (buf, "%%%02x", (unsigned char) *src++);
 | 
						|
  left -= n;
 | 
						|
  if (left <= 0)
 | 
						|
    return true;
 | 
						|
  strcpy (d, buf);
 | 
						|
  d += n;
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
mount_item::fnmunge (char *dst, const char *src, int& left)
 | 
						|
{
 | 
						|
  int name_type;
 | 
						|
  if (!(name_type = special_name (src)))
 | 
						|
    {
 | 
						|
      if ((int) strlen (src) >= left)
 | 
						|
	return ENAMETOOLONG;
 | 
						|
      else
 | 
						|
	strcpy (dst, src);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      char *d = dst;
 | 
						|
      if (copy1 (d, src, left))
 | 
						|
	  return ENAMETOOLONG;
 | 
						|
      if (name_type < 0 && copyenc (d, src, left))
 | 
						|
	return ENAMETOOLONG;
 | 
						|
 | 
						|
      while (*src)
 | 
						|
	if (!strchr (special_chars, *src) || (*src == '%' && !special_char (src)))
 | 
						|
	  {
 | 
						|
	    if (copy1 (d, src, left))
 | 
						|
	      return ENAMETOOLONG;
 | 
						|
	  }
 | 
						|
	else if (copyenc (d, src, left))
 | 
						|
	  return ENAMETOOLONG;
 | 
						|
 | 
						|
      char dot[] = ".";
 | 
						|
      const char *p = dot;
 | 
						|
      if (*--d != '.')
 | 
						|
	d++;
 | 
						|
      else if (copyenc (d, p, left))
 | 
						|
	return ENAMETOOLONG;
 | 
						|
 | 
						|
      *d = *src;
 | 
						|
    }
 | 
						|
 | 
						|
  backslashify (dst, dst, 0);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
mount_item::build_win32 (char *dst, const char *src, unsigned *outflags, unsigned chroot_pathlen)
 | 
						|
{
 | 
						|
  int n, err = 0;
 | 
						|
  const char *real_native_path;
 | 
						|
  int real_posix_pathlen;
 | 
						|
  set_flags (outflags, (unsigned) flags);
 | 
						|
  if (!cygheap->root.exists () || posix_pathlen != 1 || posix_path[0] != '/')
 | 
						|
    {
 | 
						|
      n = native_pathlen;
 | 
						|
      real_native_path = native_path;
 | 
						|
      real_posix_pathlen = chroot_pathlen ?: posix_pathlen;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      n = cygheap->root.native_length ();
 | 
						|
      real_native_path = cygheap->root.native_path ();
 | 
						|
      real_posix_pathlen = posix_pathlen;
 | 
						|
    }
 | 
						|
  memcpy (dst, real_native_path, n + 1);
 | 
						|
  const char *p = src + real_posix_pathlen;
 | 
						|
  if (*p == '/')
 | 
						|
    /* nothing */;
 | 
						|
  else if ((!(flags & MOUNT_ENC) && isdrive (dst) && !dst[2]) || *p)
 | 
						|
    dst[n++] = '\\';
 | 
						|
  if (!*p || !(flags & MOUNT_ENC))
 | 
						|
    {
 | 
						|
      if ((n + strlen (p)) >= CYG_MAX_PATH)
 | 
						|
	err = ENAMETOOLONG;
 | 
						|
      else
 | 
						|
	backslashify (p, dst + n, 0);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      int left = CYG_MAX_PATH - n;
 | 
						|
      while (*p)
 | 
						|
	{
 | 
						|
	  char slash = 0;
 | 
						|
	  char *s = strchr (p + 1, '/');
 | 
						|
	  if (s)
 | 
						|
	    {
 | 
						|
	      slash = *s;
 | 
						|
	      *s = '\0';
 | 
						|
	    }
 | 
						|
	  err = fnmunge (dst += n, p, left);
 | 
						|
	  if (!s || err)
 | 
						|
	    break;
 | 
						|
	  n = strlen (dst);
 | 
						|
	  *s = slash;
 | 
						|
	  p = s;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return err;
 | 
						|
}
 | 
						|
 | 
						|
/* conv_to_win32_path: Ensure src_path is a pure Win32 path and store
 | 
						|
   the result in win32_path.
 | 
						|
 | 
						|
   If win32_path != NULL, the relative path, if possible to keep, is
 | 
						|
   stored in win32_path.  If the relative path isn't possible to keep,
 | 
						|
   the full path is stored.
 | 
						|
 | 
						|
   If full_win32_path != NULL, the full path is stored there.
 | 
						|
 | 
						|
   The result is zero for success, or an errno value.
 | 
						|
 | 
						|
   {,full_}win32_path must have sufficient space (i.e. CYG_MAX_PATH bytes).  */
 | 
						|
 | 
						|
int
 | 
						|
mount_info::conv_to_win32_path (const char *src_path, char *dst, device& dev,
 | 
						|
				unsigned *flags)
 | 
						|
{
 | 
						|
  bool chroot_ok = !cygheap->root.exists ();
 | 
						|
  while (sys_mount_table_counter < cygwin_shared->sys_mount_table_counter)
 | 
						|
    {
 | 
						|
      int current = cygwin_shared->sys_mount_table_counter;
 | 
						|
      init ();
 | 
						|
      sys_mount_table_counter = current;
 | 
						|
    }
 | 
						|
  MALLOC_CHECK;
 | 
						|
 | 
						|
  dev.devn = FH_FS;
 | 
						|
 | 
						|
  *flags = 0;
 | 
						|
  debug_printf ("conv_to_win32_path (%s)", src_path);
 | 
						|
 | 
						|
  int i, rc;
 | 
						|
  mount_item *mi = NULL;	/* initialized to avoid compiler warning */
 | 
						|
 | 
						|
  /* The path is already normalized, without ../../ stuff, we need to have this
 | 
						|
     so that we can move from one mounted directory to another with relative
 | 
						|
     stuff.
 | 
						|
 | 
						|
     eg mounting c:/foo /foo
 | 
						|
     d:/bar /bar
 | 
						|
 | 
						|
     cd /bar
 | 
						|
     ls ../foo
 | 
						|
 | 
						|
     should look in c:/foo, not d:/foo.
 | 
						|
 | 
						|
     converting normalizex UNIX path to a DOS-style path, looking up the
 | 
						|
     appropriate drive in the mount table.  */
 | 
						|
 | 
						|
  /* See if this is a cygwin "device" */
 | 
						|
  if (win32_device_name (src_path, dst, dev))
 | 
						|
    {
 | 
						|
      *flags = MOUNT_BINARY;	/* FIXME: Is this a sensible default for devices? */
 | 
						|
      rc = 0;
 | 
						|
      goto out_no_chroot_check;
 | 
						|
    }
 | 
						|
 | 
						|
  MALLOC_CHECK;
 | 
						|
  /* If the path is on a network drive, bypass the mount table.
 | 
						|
     If it's // or //MACHINE, use the netdrive device. */
 | 
						|
  if (src_path[1] == '/')
 | 
						|
    {
 | 
						|
      if (!strchr (src_path + 2, '/'))
 | 
						|
	{
 | 
						|
	  dev = *netdrive_dev;
 | 
						|
	  set_flags (flags, PATH_BINARY);
 | 
						|
	}
 | 
						|
      backslashify (src_path, dst, 0);
 | 
						|
      /* Go through chroot check */
 | 
						|
      goto out;
 | 
						|
    }
 | 
						|
  if (isproc (src_path))
 | 
						|
    {
 | 
						|
      dev = *proc_dev;
 | 
						|
      dev.devn = fhandler_proc::get_proc_fhandler (src_path);
 | 
						|
      if (dev.devn == FH_BAD)
 | 
						|
	return ENOENT;
 | 
						|
      set_flags (flags, PATH_BINARY);
 | 
						|
      strcpy (dst, src_path);
 | 
						|
      goto out;
 | 
						|
    }
 | 
						|
  /* Check if the cygdrive prefix was specified.  If so, just strip
 | 
						|
     off the prefix and transform it into an MS-DOS path. */
 | 
						|
  else if (iscygdrive (src_path))
 | 
						|
    {
 | 
						|
      int n = mount_table->cygdrive_len - 1;
 | 
						|
      int unit;
 | 
						|
 | 
						|
      if (!src_path[n])
 | 
						|
	{
 | 
						|
	  unit = 0;
 | 
						|
	  dst[0] = '\0';
 | 
						|
	  if (mount_table->cygdrive_len > 1)
 | 
						|
	    dev = *cygdrive_dev;
 | 
						|
	}
 | 
						|
      else if (cygdrive_win32_path (src_path, dst, unit))
 | 
						|
	{
 | 
						|
	  set_flags (flags, (unsigned) cygdrive_flags);
 | 
						|
	  goto out;
 | 
						|
	}
 | 
						|
      else if (mount_table->cygdrive_len > 1)
 | 
						|
	return ENOENT;
 | 
						|
    }
 | 
						|
 | 
						|
  int chroot_pathlen;
 | 
						|
  chroot_pathlen = 0;
 | 
						|
  /* Check the mount table for prefix matches. */
 | 
						|
  for (i = 0; i < nmounts; i++)
 | 
						|
    {
 | 
						|
      const char *path;
 | 
						|
      int len;
 | 
						|
 | 
						|
      mi = mount + posix_sorted[i];
 | 
						|
      if (!cygheap->root.exists ()
 | 
						|
	  || (mi->posix_pathlen == 1 && mi->posix_path[0] == '/'))
 | 
						|
	{
 | 
						|
	  path = mi->posix_path;
 | 
						|
	  len = mi->posix_pathlen;
 | 
						|
	}
 | 
						|
      else if (cygheap->root.posix_ok (mi->posix_path))
 | 
						|
	{
 | 
						|
	  path = cygheap->root.unchroot (mi->posix_path);
 | 
						|
	  chroot_pathlen = len = strlen (path);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  chroot_pathlen = 0;
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
 | 
						|
      if (path_prefix_p (path, src_path, len))
 | 
						|
	break;
 | 
						|
    }
 | 
						|
 | 
						|
  if (i < nmounts)
 | 
						|
    {
 | 
						|
      int err = mi->build_win32 (dst, src_path, flags, chroot_pathlen);
 | 
						|
      if (err)
 | 
						|
	return err;
 | 
						|
      chroot_ok = true;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      int offset = 0;
 | 
						|
      if (src_path[1] != '/' && src_path[1] != ':')
 | 
						|
	offset = cygheap->cwd.get_drive (dst);
 | 
						|
      backslashify (src_path, dst + offset, 0);
 | 
						|
    }
 | 
						|
 out:
 | 
						|
  MALLOC_CHECK;
 | 
						|
  if (chroot_ok || cygheap->root.ischroot_native (dst))
 | 
						|
    rc = 0;
 | 
						|
  else
 | 
						|
    {
 | 
						|
      debug_printf ("attempt to access outside of chroot '%s - %s'",
 | 
						|
		    cygheap->root.posix_path (), cygheap->root.native_path ());
 | 
						|
      rc = ENOENT;
 | 
						|
    }
 | 
						|
 | 
						|
 out_no_chroot_check:
 | 
						|
  debug_printf ("src_path %s, dst %s, flags %p, rc %d", src_path, dst, *flags, rc);
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
/* cygdrive_posix_path: Build POSIX path used as the
 | 
						|
   mount point for cygdrives created when there is no other way to
 | 
						|
   obtain a POSIX path from a Win32 one. */
 | 
						|
 | 
						|
void
 | 
						|
mount_info::cygdrive_posix_path (const char *src, char *dst, int trailing_slash_p)
 | 
						|
{
 | 
						|
  int len = cygdrive_len;
 | 
						|
 | 
						|
  memcpy (dst, cygdrive, len + 1);
 | 
						|
 | 
						|
  /* Now finish the path off with the drive letter to be used.
 | 
						|
     The cygdrive prefix always ends with a trailing slash so
 | 
						|
     the drive letter is added after the path. */
 | 
						|
  dst[len++] = cyg_tolower (src[0]);
 | 
						|
  if (!src[2] || (isdirsep (src[2]) && !src[3]))
 | 
						|
    dst[len++] = '\000';
 | 
						|
  else
 | 
						|
    {
 | 
						|
      int n;
 | 
						|
      dst[len++] = '/';
 | 
						|
      if (isdirsep (src[2]))
 | 
						|
	n = 3;
 | 
						|
      else
 | 
						|
	n = 2;
 | 
						|
      strcpy (dst + len, src + n);
 | 
						|
    }
 | 
						|
  slashify (dst, dst, trailing_slash_p);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
mount_info::cygdrive_win32_path (const char *src, char *dst, int& unit)
 | 
						|
{
 | 
						|
  int res;
 | 
						|
  const char *p = src + cygdrive_len;
 | 
						|
  if (!isalpha (*p) || (!isdirsep (p[1]) && p[1]))
 | 
						|
    {
 | 
						|
      unit = -1; /* FIXME: should be zero, maybe? */
 | 
						|
      dst[0] = '\0';
 | 
						|
      res = 0;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      dst[0] = cyg_tolower (*p);
 | 
						|
      dst[1] = ':';
 | 
						|
      strcpy (dst + 2, p + 1);
 | 
						|
      backslashify (dst, dst, !dst[2]);
 | 
						|
      unit = dst[0];
 | 
						|
      res = 1;
 | 
						|
    }
 | 
						|
  debug_printf ("src '%s', dst '%s'", src, dst);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
/* conv_to_posix_path: Ensure src_path is a POSIX path.
 | 
						|
 | 
						|
   The result is zero for success, or an errno value.
 | 
						|
   posix_path must have sufficient space (i.e. CYG_MAX_PATH bytes).
 | 
						|
   If keep_rel_p is non-zero, relative paths stay that way.  */
 | 
						|
 | 
						|
int
 | 
						|
mount_info::conv_to_posix_path (const char *src_path, char *posix_path,
 | 
						|
				int keep_rel_p)
 | 
						|
{
 | 
						|
  int src_path_len = strlen (src_path);
 | 
						|
  int relative_path_p = !isabspath (src_path);
 | 
						|
  int trailing_slash_p;
 | 
						|
 | 
						|
  if (src_path_len <= 1)
 | 
						|
    trailing_slash_p = 0;
 | 
						|
  else
 | 
						|
    {
 | 
						|
      const char *lastchar = src_path + src_path_len - 1;
 | 
						|
      trailing_slash_p = isdirsep (*lastchar) && lastchar[-1] != ':';
 | 
						|
    }
 | 
						|
 | 
						|
  debug_printf ("conv_to_posix_path (%s, %s, %s)", src_path,
 | 
						|
		keep_rel_p ? "keep-rel" : "no-keep-rel",
 | 
						|
		trailing_slash_p ? "add-slash" : "no-add-slash");
 | 
						|
  MALLOC_CHECK;
 | 
						|
 | 
						|
  if (src_path_len >= CYG_MAX_PATH)
 | 
						|
    {
 | 
						|
      debug_printf ("ENAMETOOLONG");
 | 
						|
      return ENAMETOOLONG;
 | 
						|
    }
 | 
						|
 | 
						|
  /* FIXME: For now, if the path is relative and it's supposed to stay
 | 
						|
     that way, skip mount table processing. */
 | 
						|
 | 
						|
  if (keep_rel_p && relative_path_p)
 | 
						|
    {
 | 
						|
      slashify (src_path, posix_path, 0);
 | 
						|
      debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
  char pathbuf[CYG_MAX_PATH];
 | 
						|
  char *tail;
 | 
						|
  int rc = normalize_win32_path (src_path, pathbuf, tail);
 | 
						|
  if (rc != 0)
 | 
						|
    {
 | 
						|
      debug_printf ("%d = conv_to_posix_path (%s)", rc, src_path);
 | 
						|
      return rc;
 | 
						|
    }
 | 
						|
 | 
						|
  int pathbuflen = tail - pathbuf;
 | 
						|
  for (int i = 0; i < nmounts; ++i)
 | 
						|
    {
 | 
						|
      mount_item &mi = mount[native_sorted[i]];
 | 
						|
      if (!path_prefix_p (mi.native_path, pathbuf, mi.native_pathlen))
 | 
						|
	continue;
 | 
						|
 | 
						|
      if (cygheap->root.exists () && !cygheap->root.posix_ok (mi.posix_path))
 | 
						|
	continue;
 | 
						|
 | 
						|
      /* SRC_PATH is in the mount table. */
 | 
						|
      int nextchar;
 | 
						|
      const char *p = pathbuf + mi.native_pathlen;
 | 
						|
 | 
						|
      if (!*p || !p[1])
 | 
						|
	nextchar = 0;
 | 
						|
      else if (isdirsep (*p))
 | 
						|
	nextchar = -1;
 | 
						|
      else
 | 
						|
	nextchar = 1;
 | 
						|
 | 
						|
      int addslash = nextchar > 0 ? 1 : 0;
 | 
						|
      if ((mi.posix_pathlen + (pathbuflen - mi.native_pathlen) + addslash) >= CYG_MAX_PATH)
 | 
						|
	return ENAMETOOLONG;
 | 
						|
      strcpy (posix_path, mi.posix_path);
 | 
						|
      if (addslash)
 | 
						|
	strcat (posix_path, "/");
 | 
						|
      if (nextchar)
 | 
						|
	slashify (p,
 | 
						|
		  posix_path + addslash + (mi.posix_pathlen == 1 ? 0 : mi.posix_pathlen),
 | 
						|
		  trailing_slash_p);
 | 
						|
 | 
						|
      if (cygheap->root.exists ())
 | 
						|
	{
 | 
						|
	  const char *p = cygheap->root.unchroot (posix_path);
 | 
						|
	  memmove (posix_path, p, strlen (p) + 1);
 | 
						|
	}
 | 
						|
      if (mi.flags & MOUNT_ENC)
 | 
						|
	{
 | 
						|
	  char tmpbuf[CYG_MAX_PATH];
 | 
						|
	  if (fnunmunge (tmpbuf, posix_path))
 | 
						|
	    strcpy (posix_path, tmpbuf);
 | 
						|
	}
 | 
						|
      goto out;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!cygheap->root.exists ())
 | 
						|
    /* nothing */;
 | 
						|
  else if (!cygheap->root.ischroot_native (pathbuf))
 | 
						|
    return ENOENT;
 | 
						|
  else
 | 
						|
    {
 | 
						|
      const char *p = pathbuf + cygheap->root.native_length ();
 | 
						|
      if (*p)
 | 
						|
	slashify (p, posix_path, trailing_slash_p);
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  posix_path[0] = '/';
 | 
						|
	  posix_path[1] = '\0';
 | 
						|
	}
 | 
						|
      goto out;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Not in the database.  This should [theoretically] only happen if either
 | 
						|
     the path begins with //, or / isn't mounted, or the path has a drive
 | 
						|
     letter not covered by the mount table.  If it's a relative path then the
 | 
						|
     caller must want an absolute path (otherwise we would have returned
 | 
						|
     above).  So we always return an absolute path at this point. */
 | 
						|
  if (isdrive (pathbuf))
 | 
						|
    cygdrive_posix_path (pathbuf, posix_path, trailing_slash_p);
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* The use of src_path and not pathbuf here is intentional.
 | 
						|
	 We couldn't translate the path, so just ensure no \'s are present. */
 | 
						|
      slashify (src_path, posix_path, trailing_slash_p);
 | 
						|
    }
 | 
						|
 | 
						|
out:
 | 
						|
  debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
 | 
						|
  MALLOC_CHECK;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Return flags associated with a mount point given the win32 path. */
 | 
						|
 | 
						|
unsigned
 | 
						|
mount_info::set_flags_from_win32_path (const char *p)
 | 
						|
{
 | 
						|
  for (int i = 0; i < nmounts; i++)
 | 
						|
    {
 | 
						|
      mount_item &mi = mount[native_sorted[i]];
 | 
						|
      if (path_prefix_p (mi.native_path, p, mi.native_pathlen))
 | 
						|
	return mi.flags;
 | 
						|
    }
 | 
						|
  return PATH_BINARY;
 | 
						|
}
 | 
						|
 | 
						|
/* read_mounts: Given a specific regkey, read mounts from under its
 | 
						|
   key. */
 | 
						|
 | 
						|
void
 | 
						|
mount_info::read_mounts (reg_key& r)
 | 
						|
{
 | 
						|
  char posix_path[CYG_MAX_PATH];
 | 
						|
  HKEY key = r.get_key ();
 | 
						|
  DWORD i, posix_path_size;
 | 
						|
  int res;
 | 
						|
 | 
						|
  /* Loop through subkeys */
 | 
						|
  /* FIXME: we would like to not check MAX_MOUNTS but the heap in the
 | 
						|
     shared area is currently statically allocated so we can't have an
 | 
						|
     arbitrarily large number of mounts. */
 | 
						|
  for (i = 0; ; i++)
 | 
						|
    {
 | 
						|
      char native_path[CYG_MAX_PATH];
 | 
						|
      int mount_flags;
 | 
						|
 | 
						|
      posix_path_size = sizeof (posix_path);
 | 
						|
      /* FIXME: if maximum posix_path_size is 256, we're going to
 | 
						|
	 run into problems if we ever try to store a mount point that's
 | 
						|
	 over 256 but is under CYG_MAX_PATH. */
 | 
						|
      res = RegEnumKeyEx (key, i, posix_path, &posix_path_size, NULL,
 | 
						|
			  NULL, NULL, NULL);
 | 
						|
 | 
						|
      if (res == ERROR_NO_MORE_ITEMS)
 | 
						|
	break;
 | 
						|
      else if (res != ERROR_SUCCESS)
 | 
						|
	{
 | 
						|
	  debug_printf ("RegEnumKeyEx failed, error %d!", res);
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
 | 
						|
      /* Get a reg_key based on i. */
 | 
						|
      reg_key subkey = reg_key (key, KEY_READ, posix_path, NULL);
 | 
						|
 | 
						|
      /* Fetch info from the subkey. */
 | 
						|
      subkey.get_string ("native", native_path, sizeof (native_path), "");
 | 
						|
      mount_flags = subkey.get_int ("flags", 0);
 | 
						|
 | 
						|
      /* Add mount_item corresponding to registry mount point. */
 | 
						|
      res = mount_table->add_item (native_path, posix_path, mount_flags, false);
 | 
						|
      if (res && get_errno () == EMFILE)
 | 
						|
	break; /* The number of entries exceeds MAX_MOUNTS */
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* from_registry: Build the entire mount table from the registry.  Also,
 | 
						|
   read in cygdrive-related information from its registry location. */
 | 
						|
 | 
						|
void
 | 
						|
mount_info::from_registry ()
 | 
						|
{
 | 
						|
 | 
						|
  /* Retrieve cygdrive-related information. */
 | 
						|
  read_cygdrive_info_from_registry ();
 | 
						|
 | 
						|
  nmounts = 0;
 | 
						|
 | 
						|
  /* First read mounts from user's table.
 | 
						|
     Then read mounts from system-wide mount table while deimpersonated . */
 | 
						|
  for (int i = 0; i < 2; i++)
 | 
						|
    {
 | 
						|
      if (i)
 | 
						|
	cygheap->user.deimpersonate ();
 | 
						|
      reg_key r (i, KEY_READ, CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, NULL);
 | 
						|
      read_mounts (r);
 | 
						|
      if (i)
 | 
						|
	cygheap->user.reimpersonate ();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* add_reg_mount: Add mount item to registry.  Return zero on success,
 | 
						|
   non-zero on failure. */
 | 
						|
/* FIXME: Need a mutex to avoid collisions with other tasks. */
 | 
						|
 | 
						|
int
 | 
						|
mount_info::add_reg_mount (const char *native_path, const char *posix_path, unsigned mountflags)
 | 
						|
{
 | 
						|
  int res;
 | 
						|
 | 
						|
  /* Add the mount to the right registry location, depending on
 | 
						|
     whether MOUNT_SYSTEM is set in the mount flags. */
 | 
						|
 | 
						|
  reg_key reg (mountflags & MOUNT_SYSTEM,  KEY_ALL_ACCESS,
 | 
						|
	       CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, NULL);
 | 
						|
 | 
						|
  /* Start by deleting existing mount if one exists. */
 | 
						|
  res = reg.kill (posix_path);
 | 
						|
  if (res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND)
 | 
						|
    {
 | 
						|
 err:
 | 
						|
      __seterrno_from_win_error (res);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Create the new mount. */
 | 
						|
  reg_key subkey (reg.get_key (), KEY_ALL_ACCESS, posix_path, NULL);
 | 
						|
 | 
						|
  res = subkey.set_string ("native", native_path);
 | 
						|
  if (res != ERROR_SUCCESS)
 | 
						|
    goto err;
 | 
						|
  res = subkey.set_int ("flags", mountflags);
 | 
						|
 | 
						|
  if (mountflags & MOUNT_SYSTEM)
 | 
						|
    {
 | 
						|
      sys_mount_table_counter++;
 | 
						|
      cygwin_shared->sys_mount_table_counter++;
 | 
						|
    }
 | 
						|
  return 0; /* Success */
 | 
						|
}
 | 
						|
 | 
						|
/* del_reg_mount: delete mount item from registry indicated in flags.
 | 
						|
   Return zero on success, non-zero on failure.*/
 | 
						|
/* FIXME: Need a mutex to avoid collisions with other tasks. */
 | 
						|
 | 
						|
int
 | 
						|
mount_info::del_reg_mount (const char * posix_path, unsigned flags)
 | 
						|
{
 | 
						|
  int res;
 | 
						|
 | 
						|
  reg_key reg (flags & MOUNT_SYSTEM, KEY_ALL_ACCESS,
 | 
						|
	       CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, NULL);
 | 
						|
  res = reg.kill (posix_path);
 | 
						|
 | 
						|
  if (res != ERROR_SUCCESS)
 | 
						|
    {
 | 
						|
      __seterrno_from_win_error (res);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  if (flags & MOUNT_SYSTEM)
 | 
						|
    {
 | 
						|
      sys_mount_table_counter++;
 | 
						|
      cygwin_shared->sys_mount_table_counter++;
 | 
						|
    }
 | 
						|
 | 
						|
  return 0; /* Success */
 | 
						|
}
 | 
						|
 | 
						|
/* read_cygdrive_info_from_registry: Read the default prefix and flags
 | 
						|
   to use when creating cygdrives from the special user registry
 | 
						|
   location used to store cygdrive information. */
 | 
						|
 | 
						|
void
 | 
						|
mount_info::read_cygdrive_info_from_registry ()
 | 
						|
{
 | 
						|
  /* First read cygdrive from user's registry.
 | 
						|
     If failed, then read cygdrive from system-wide registry
 | 
						|
     while deimpersonated. */
 | 
						|
  for (int i = 0; i < 2; i++)
 | 
						|
    {
 | 
						|
      if (i)
 | 
						|
	cygheap->user.deimpersonate ();
 | 
						|
      reg_key r (i, KEY_READ, CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, NULL);
 | 
						|
      if (i)
 | 
						|
	cygheap->user.reimpersonate ();
 | 
						|
 | 
						|
      if (r.get_string (CYGWIN_INFO_CYGDRIVE_PREFIX, cygdrive, sizeof (cygdrive),
 | 
						|
			CYGWIN_INFO_CYGDRIVE_DEFAULT_PREFIX) != ERROR_SUCCESS && i == 0)
 | 
						|
	continue;
 | 
						|
 | 
						|
      /* Fetch user cygdrive_flags from registry; returns MOUNT_CYGDRIVE on error. */
 | 
						|
      cygdrive_flags = r.get_int (CYGWIN_INFO_CYGDRIVE_FLAGS,
 | 
						|
				  MOUNT_CYGDRIVE | MOUNT_BINARY);
 | 
						|
      /* Sanitize */
 | 
						|
      if (i == 0)
 | 
						|
	cygdrive_flags &= ~MOUNT_SYSTEM;
 | 
						|
      else
 | 
						|
	cygdrive_flags |= MOUNT_SYSTEM;
 | 
						|
      slashify (cygdrive, cygdrive, 1);
 | 
						|
      cygdrive_len = strlen (cygdrive);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* write_cygdrive_info_to_registry: Write the default prefix and flags
 | 
						|
   to use when creating cygdrives to the special user registry
 | 
						|
   location used to store cygdrive information. */
 | 
						|
 | 
						|
int
 | 
						|
mount_info::write_cygdrive_info_to_registry (const char *cygdrive_prefix, unsigned flags)
 | 
						|
{
 | 
						|
  /* Verify cygdrive prefix starts with a forward slash and if there's
 | 
						|
     another character, it's not a slash. */
 | 
						|
  if ((cygdrive_prefix == NULL) || (*cygdrive_prefix == 0) ||
 | 
						|
      (!isslash (cygdrive_prefix[0])) ||
 | 
						|
      ((cygdrive_prefix[1] != '\0') && (isslash (cygdrive_prefix[1]))))
 | 
						|
      {
 | 
						|
	set_errno (EINVAL);
 | 
						|
	return -1;
 | 
						|
      }
 | 
						|
 | 
						|
  char hold_cygdrive_prefix[strlen (cygdrive_prefix) + 1];
 | 
						|
  /* Ensure that there is never a final slash */
 | 
						|
  nofinalslash (cygdrive_prefix, hold_cygdrive_prefix);
 | 
						|
 | 
						|
  reg_key r (flags & MOUNT_SYSTEM, KEY_ALL_ACCESS,
 | 
						|
	     CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, NULL);
 | 
						|
  int res;
 | 
						|
  res = r.set_string (CYGWIN_INFO_CYGDRIVE_PREFIX, hold_cygdrive_prefix);
 | 
						|
  if (res != ERROR_SUCCESS)
 | 
						|
    {
 | 
						|
      __seterrno_from_win_error (res);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  r.set_int (CYGWIN_INFO_CYGDRIVE_FLAGS, flags);
 | 
						|
 | 
						|
  if (flags & MOUNT_SYSTEM)
 | 
						|
    sys_mount_table_counter = ++cygwin_shared->sys_mount_table_counter;
 | 
						|
 | 
						|
  /* This also needs to go in the in-memory copy of "cygdrive", but only if
 | 
						|
     appropriate:
 | 
						|
       1. setting user path prefix, or
 | 
						|
       2. overwriting (a previous) system path prefix */
 | 
						|
  if (!(flags & MOUNT_SYSTEM) || (mount_table->cygdrive_flags & MOUNT_SYSTEM))
 | 
						|
    {
 | 
						|
      slashify (cygdrive_prefix, cygdrive, 1);
 | 
						|
      cygdrive_flags = flags;
 | 
						|
      cygdrive_len = strlen (cygdrive);
 | 
						|
    }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
mount_info::remove_cygdrive_info_from_registry (const char *cygdrive_prefix, unsigned flags)
 | 
						|
{
 | 
						|
  reg_key r (flags & MOUNT_SYSTEM, KEY_ALL_ACCESS,
 | 
						|
	     CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
 | 
						|
	     NULL);
 | 
						|
 | 
						|
  /* Delete cygdrive prefix and flags. */
 | 
						|
  int res = r.killvalue (CYGWIN_INFO_CYGDRIVE_PREFIX);
 | 
						|
  int res2 = r.killvalue (CYGWIN_INFO_CYGDRIVE_FLAGS);
 | 
						|
 | 
						|
  if (flags & MOUNT_SYSTEM)
 | 
						|
    sys_mount_table_counter = ++cygwin_shared->sys_mount_table_counter;
 | 
						|
 | 
						|
  /* Reinitialize the cygdrive path prefix to reflect to removal from the
 | 
						|
     registry. */
 | 
						|
  read_cygdrive_info_from_registry ();
 | 
						|
 | 
						|
  if (res == ERROR_SUCCESS)
 | 
						|
    res = res2;
 | 
						|
  if (res == ERROR_SUCCESS)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  __seterrno_from_win_error (res);
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
mount_info::get_cygdrive_info (char *user, char *system, char* user_flags,
 | 
						|
			       char* system_flags)
 | 
						|
{
 | 
						|
  /* Get the user path prefix from HKEY_CURRENT_USER. */
 | 
						|
  reg_key r (false,  KEY_READ, CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, NULL);
 | 
						|
  int res = r.get_string (CYGWIN_INFO_CYGDRIVE_PREFIX, user, CYG_MAX_PATH, "");
 | 
						|
 | 
						|
  /* Get the user flags, if appropriate */
 | 
						|
  if (res == ERROR_SUCCESS)
 | 
						|
    {
 | 
						|
      int flags = r.get_int (CYGWIN_INFO_CYGDRIVE_FLAGS, MOUNT_CYGDRIVE | MOUNT_BINARY);
 | 
						|
      strcpy (user_flags, (flags & MOUNT_BINARY) ? "binmode" : "textmode");
 | 
						|
    }
 | 
						|
 | 
						|
  /* Get the system path prefix from HKEY_LOCAL_MACHINE. */
 | 
						|
  reg_key r2 (true,  KEY_READ, CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, NULL);
 | 
						|
  int res2 = r2.get_string (CYGWIN_INFO_CYGDRIVE_PREFIX, system, CYG_MAX_PATH, "");
 | 
						|
 | 
						|
  /* Get the system flags, if appropriate */
 | 
						|
  if (res2 == ERROR_SUCCESS)
 | 
						|
    {
 | 
						|
      int flags = r2.get_int (CYGWIN_INFO_CYGDRIVE_FLAGS, MOUNT_CYGDRIVE | MOUNT_BINARY);
 | 
						|
      strcpy (system_flags, (flags & MOUNT_BINARY) ? "binmode" : "textmode");
 | 
						|
    }
 | 
						|
 | 
						|
  return (res != ERROR_SUCCESS) ? res : res2;
 | 
						|
}
 | 
						|
 | 
						|
static mount_item *mounts_for_sort;
 | 
						|
 | 
						|
/* sort_by_posix_name: qsort callback to sort the mount entries.  Sort
 | 
						|
   user mounts ahead of system mounts to the same POSIX path. */
 | 
						|
/* FIXME: should the user should be able to choose whether to
 | 
						|
   prefer user or system mounts??? */
 | 
						|
static int
 | 
						|
sort_by_posix_name (const void *a, const void *b)
 | 
						|
{
 | 
						|
  mount_item *ap = mounts_for_sort + (*((int*) a));
 | 
						|
  mount_item *bp = mounts_for_sort + (*((int*) b));
 | 
						|
 | 
						|
  /* Base weighting on longest posix path first so that the most
 | 
						|
     obvious path will be chosen. */
 | 
						|
  size_t alen = strlen (ap->posix_path);
 | 
						|
  size_t blen = strlen (bp->posix_path);
 | 
						|
 | 
						|
  int res = blen - alen;
 | 
						|
 | 
						|
  if (res)
 | 
						|
    return res;		/* Path lengths differed */
 | 
						|
 | 
						|
  /* The two paths were the same length, so just determine normal
 | 
						|
     lexical sorted order. */
 | 
						|
  res = strcmp (ap->posix_path, bp->posix_path);
 | 
						|
 | 
						|
  if (res == 0)
 | 
						|
   {
 | 
						|
     /* need to select between user and system mount to same POSIX path */
 | 
						|
     if (!(bp->flags & MOUNT_SYSTEM))	/* user mount */
 | 
						|
      return 1;
 | 
						|
     else
 | 
						|
      return -1;
 | 
						|
   }
 | 
						|
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
/* sort_by_native_name: qsort callback to sort the mount entries.  Sort
 | 
						|
   user mounts ahead of system mounts to the same POSIX path. */
 | 
						|
/* FIXME: should the user should be able to choose whether to
 | 
						|
   prefer user or system mounts??? */
 | 
						|
static int
 | 
						|
sort_by_native_name (const void *a, const void *b)
 | 
						|
{
 | 
						|
  mount_item *ap = mounts_for_sort + (*((int*) a));
 | 
						|
  mount_item *bp = mounts_for_sort + (*((int*) b));
 | 
						|
 | 
						|
  /* Base weighting on longest win32 path first so that the most
 | 
						|
     obvious path will be chosen. */
 | 
						|
  size_t alen = strlen (ap->native_path);
 | 
						|
  size_t blen = strlen (bp->native_path);
 | 
						|
 | 
						|
  int res = blen - alen;
 | 
						|
 | 
						|
  if (res)
 | 
						|
    return res;		/* Path lengths differed */
 | 
						|
 | 
						|
  /* The two paths were the same length, so just determine normal
 | 
						|
     lexical sorted order. */
 | 
						|
  res = strcmp (ap->native_path, bp->native_path);
 | 
						|
 | 
						|
  if (res == 0)
 | 
						|
   {
 | 
						|
     /* need to select between user and system mount to same POSIX path */
 | 
						|
     if (!(bp->flags & MOUNT_SYSTEM))	/* user mount */
 | 
						|
      return 1;
 | 
						|
     else
 | 
						|
      return -1;
 | 
						|
   }
 | 
						|
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
mount_info::sort ()
 | 
						|
{
 | 
						|
  for (int i = 0; i < nmounts; i++)
 | 
						|
    native_sorted[i] = posix_sorted[i] = i;
 | 
						|
  /* Sort them into reverse length order, otherwise we won't
 | 
						|
     be able to look for /foo in /.  */
 | 
						|
  mounts_for_sort = mount;	/* ouch. */
 | 
						|
  qsort (posix_sorted, nmounts, sizeof (posix_sorted[0]), sort_by_posix_name);
 | 
						|
  qsort (native_sorted, nmounts, sizeof (native_sorted[0]), sort_by_native_name);
 | 
						|
}
 | 
						|
 | 
						|
/* Add an entry to the mount table.
 | 
						|
   Returns 0 on success, -1 on failure and errno is set.
 | 
						|
 | 
						|
   This is where all argument validation is done.  It may not make sense to
 | 
						|
   do this when called internally, but it's cleaner to keep it all here.  */
 | 
						|
 | 
						|
int
 | 
						|
mount_info::add_item (const char *native, const char *posix, unsigned mountflags, int reg_p)
 | 
						|
{
 | 
						|
  char nativetmp[CYG_MAX_PATH];
 | 
						|
  char posixtmp[CYG_MAX_PATH];
 | 
						|
  char *nativetail, *posixtail, error[] = "error";
 | 
						|
  int nativeerr, posixerr;
 | 
						|
 | 
						|
  /* Something's wrong if either path is NULL or empty, or if it's
 | 
						|
     not a UNC or absolute path. */
 | 
						|
 | 
						|
  if (native == NULL || !isabspath (native) ||
 | 
						|
      !(is_unc_share (native) || isdrive (native)))
 | 
						|
    nativeerr = EINVAL;
 | 
						|
  else
 | 
						|
    nativeerr = normalize_win32_path (native, nativetmp, nativetail);
 | 
						|
 | 
						|
  if (posix == NULL || !isabspath (posix) ||
 | 
						|
      is_unc_share (posix) || isdrive (posix))
 | 
						|
    posixerr = EINVAL;
 | 
						|
  else
 | 
						|
    posixerr = normalize_posix_path (posix, posixtmp, posixtail);
 | 
						|
 | 
						|
  debug_printf ("%s[%s], %s[%s], %p",
 | 
						|
		native, nativeerr ? error : nativetmp,
 | 
						|
		posix, posixerr ? error : posixtmp, mountflags);
 | 
						|
 | 
						|
  if (nativeerr || posixerr)
 | 
						|
    {
 | 
						|
      set_errno (nativeerr?:posixerr);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Make sure both paths do not end in /. */
 | 
						|
  if (nativetail > nativetmp + 1 && nativetail[-1] == '\\')
 | 
						|
    nativetail[-1] = '\0';
 | 
						|
  if (posixtail > posixtmp + 1 && posixtail[-1] == '/')
 | 
						|
    posixtail[-1] = '\0';
 | 
						|
 | 
						|
  /* Write over an existing mount item with the same POSIX path if
 | 
						|
     it exists and is from the same registry area. */
 | 
						|
  int i;
 | 
						|
  for (i = 0; i < nmounts; i++)
 | 
						|
    {
 | 
						|
      if (strcasematch (mount[i].posix_path, posixtmp) &&
 | 
						|
	  (mount[i].flags & MOUNT_SYSTEM) == (mountflags & MOUNT_SYSTEM))
 | 
						|
	break;
 | 
						|
    }
 | 
						|
 | 
						|
  if (i == nmounts && nmounts == MAX_MOUNTS)
 | 
						|
    {
 | 
						|
      set_errno (EMFILE);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  if (reg_p && add_reg_mount (nativetmp, posixtmp, mountflags))
 | 
						|
    return -1;
 | 
						|
 | 
						|
  if (i == nmounts)
 | 
						|
    nmounts++;
 | 
						|
  mount[i].init (nativetmp, posixtmp, mountflags);
 | 
						|
  sort ();
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Delete a mount table entry where path is either a Win32 or POSIX
 | 
						|
   path. Since the mount table is really just a table of aliases,
 | 
						|
   deleting / is ok (although running without a slash mount is
 | 
						|
   strongly discouraged because some programs may run erratically
 | 
						|
   without one).  If MOUNT_SYSTEM is set in flags, remove from system
 | 
						|
   registry, otherwise remove the user registry mount.
 | 
						|
*/
 | 
						|
 | 
						|
int
 | 
						|
mount_info::del_item (const char *path, unsigned flags, int reg_p)
 | 
						|
{
 | 
						|
  char pathtmp[CYG_MAX_PATH];
 | 
						|
  int posix_path_p = false;
 | 
						|
 | 
						|
  /* Something's wrong if path is NULL or empty. */
 | 
						|
  if (path == NULL || *path == 0 || !isabspath (path))
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  if (is_unc_share (path) || strpbrk (path, ":\\"))
 | 
						|
    backslashify (path, pathtmp, 0);
 | 
						|
  else
 | 
						|
    {
 | 
						|
      slashify (path, pathtmp, 0);
 | 
						|
      posix_path_p = true;
 | 
						|
    }
 | 
						|
  nofinalslash (pathtmp, pathtmp);
 | 
						|
 | 
						|
  if (reg_p && posix_path_p &&
 | 
						|
      del_reg_mount (pathtmp, flags) &&
 | 
						|
      del_reg_mount (path, flags)) /* for old irregular entries */
 | 
						|
    return -1;
 | 
						|
 | 
						|
  for (int i = 0; i < nmounts; i++)
 | 
						|
    {
 | 
						|
      int ent = native_sorted[i]; /* in the same order as getmntent() */
 | 
						|
      if (((posix_path_p)
 | 
						|
	   ? strcasematch (mount[ent].posix_path, pathtmp)
 | 
						|
	   : strcasematch (mount[ent].native_path, pathtmp)) &&
 | 
						|
	  (mount[ent].flags & MOUNT_SYSTEM) == (flags & MOUNT_SYSTEM))
 | 
						|
	{
 | 
						|
	  if (!posix_path_p &&
 | 
						|
	      reg_p && del_reg_mount (mount[ent].posix_path, flags))
 | 
						|
	    return -1;
 | 
						|
 | 
						|
	  nmounts--; /* One less mount table entry */
 | 
						|
	  /* Fill in the hole if not at the end of the table */
 | 
						|
	  if (ent < nmounts)
 | 
						|
	    memmove (mount + ent, mount + ent + 1,
 | 
						|
		     sizeof (mount[ent]) * (nmounts - ent));
 | 
						|
	  sort (); /* Resort the table */
 | 
						|
	  return 0;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  set_errno (EINVAL);
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
/************************* mount_item class ****************************/
 | 
						|
 | 
						|
static mntent *
 | 
						|
fillout_mntent (const char *native_path, const char *posix_path, unsigned flags)
 | 
						|
{
 | 
						|
  struct mntent& ret=_my_tls.locals.mntbuf;
 | 
						|
 | 
						|
  /* Remove drivenum from list if we see a x: style path */
 | 
						|
  if (strlen (native_path) == 2 && native_path[1] == ':')
 | 
						|
    {
 | 
						|
      int drivenum = cyg_tolower (native_path[0]) - 'a';
 | 
						|
      if (drivenum >= 0 && drivenum <= 31)
 | 
						|
	_my_tls.locals.available_drives &= ~(1 << drivenum);
 | 
						|
    }
 | 
						|
 | 
						|
  /* Pass back pointers to mount_table strings reserved for use by
 | 
						|
     getmntent rather than pointers to strings in the internal mount
 | 
						|
     table because the mount table might change, causing weird effects
 | 
						|
     from the getmntent user's point of view. */
 | 
						|
 | 
						|
  strcpy (_my_tls.locals.mnt_fsname, native_path);
 | 
						|
  ret.mnt_fsname = _my_tls.locals.mnt_fsname;
 | 
						|
  strcpy (_my_tls.locals.mnt_dir, posix_path);
 | 
						|
  ret.mnt_dir = _my_tls.locals.mnt_dir;
 | 
						|
 | 
						|
  if (!(flags & MOUNT_SYSTEM))		/* user mount */
 | 
						|
    strcpy (_my_tls.locals.mnt_type, (char *) "user");
 | 
						|
  else					/* system mount */
 | 
						|
    strcpy (_my_tls.locals.mnt_type, (char *) "system");
 | 
						|
 | 
						|
  ret.mnt_type = _my_tls.locals.mnt_type;
 | 
						|
 | 
						|
  /* mnt_opts is a string that details mount params such as
 | 
						|
     binary or textmode, or exec.  We don't print
 | 
						|
     `silent' here; it's a magic internal thing. */
 | 
						|
 | 
						|
  if (!(flags & MOUNT_BINARY))
 | 
						|
    strcpy (_my_tls.locals.mnt_opts, (char *) "textmode");
 | 
						|
  else
 | 
						|
    strcpy (_my_tls.locals.mnt_opts, (char *) "binmode");
 | 
						|
 | 
						|
  if (flags & MOUNT_CYGWIN_EXEC)
 | 
						|
    strcat (_my_tls.locals.mnt_opts, (char *) ",cygexec");
 | 
						|
  else if (flags & MOUNT_EXEC)
 | 
						|
    strcat (_my_tls.locals.mnt_opts, (char *) ",exec");
 | 
						|
  else if (flags & MOUNT_NOTEXEC)
 | 
						|
    strcat (_my_tls.locals.mnt_opts, (char *) ",noexec");
 | 
						|
  if (flags & MOUNT_ENC)
 | 
						|
    strcat (_my_tls.locals.mnt_opts, ",managed");
 | 
						|
 | 
						|
  if ((flags & MOUNT_CYGDRIVE))		/* cygdrive */
 | 
						|
    strcat (_my_tls.locals.mnt_opts, (char *) ",noumount");
 | 
						|
  ret.mnt_opts = _my_tls.locals.mnt_opts;
 | 
						|
 | 
						|
  ret.mnt_freq = 1;
 | 
						|
  ret.mnt_passno = 1;
 | 
						|
  return &ret;
 | 
						|
}
 | 
						|
 | 
						|
struct mntent *
 | 
						|
mount_item::getmntent ()
 | 
						|
{
 | 
						|
  return fillout_mntent (native_path, posix_path, flags);
 | 
						|
}
 | 
						|
 | 
						|
static struct mntent *
 | 
						|
cygdrive_getmntent ()
 | 
						|
{
 | 
						|
  char native_path[4];
 | 
						|
  char posix_path[CYG_MAX_PATH];
 | 
						|
  DWORD mask = 1, drive = 'a';
 | 
						|
  struct mntent *ret = NULL;
 | 
						|
 | 
						|
  while (_my_tls.locals.available_drives)
 | 
						|
    {
 | 
						|
      for (/* nothing */; drive <= 'z'; mask <<= 1, drive++)
 | 
						|
	if (_my_tls.locals.available_drives & mask)
 | 
						|
	  break;
 | 
						|
 | 
						|
      __small_sprintf (native_path, "%c:\\", drive);
 | 
						|
      if (GetFileAttributes (native_path) == INVALID_FILE_ATTRIBUTES)
 | 
						|
	{
 | 
						|
	  _my_tls.locals.available_drives &= ~mask;
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
      native_path[2] = '\0';
 | 
						|
      __small_sprintf (posix_path, "%s%c", mount_table->cygdrive, drive);
 | 
						|
      ret = fillout_mntent (native_path, posix_path, mount_table->cygdrive_flags);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
struct mntent *
 | 
						|
mount_info::getmntent (int x)
 | 
						|
{
 | 
						|
  if (x < 0 || x >= nmounts)
 | 
						|
    return cygdrive_getmntent ();
 | 
						|
 | 
						|
  return mount[native_sorted[x]].getmntent ();
 | 
						|
}
 | 
						|
 | 
						|
/* Fill in the fields of a mount table entry.  */
 | 
						|
 | 
						|
void
 | 
						|
mount_item::init (const char *native, const char *posix, unsigned mountflags)
 | 
						|
{
 | 
						|
  strcpy ((char *) native_path, native);
 | 
						|
  strcpy ((char *) posix_path, posix);
 | 
						|
 | 
						|
  native_pathlen = strlen (native_path);
 | 
						|
  posix_pathlen = strlen (posix_path);
 | 
						|
 | 
						|
  flags = mountflags;
 | 
						|
}
 | 
						|
 | 
						|
/********************** Mount System Calls **************************/
 | 
						|
 | 
						|
/* Mount table system calls.
 | 
						|
   Note that these are exported to the application.  */
 | 
						|
 | 
						|
/* mount: Add a mount to the mount table in memory and to the registry
 | 
						|
   that will cause paths under win32_path to be translated to paths
 | 
						|
   under posix_path. */
 | 
						|
 | 
						|
extern "C" int
 | 
						|
mount (const char *win32_path, const char *posix_path, unsigned flags)
 | 
						|
{
 | 
						|
  int res = -1;
 | 
						|
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EFAULT))
 | 
						|
    /* errno set */;
 | 
						|
  else if (!*posix_path)
 | 
						|
    set_errno (EINVAL);
 | 
						|
  else if (strpbrk (posix_path, "\\:"))
 | 
						|
    set_errno (EINVAL);
 | 
						|
  else if (flags & MOUNT_CYGDRIVE) /* normal mount */
 | 
						|
    {
 | 
						|
      /* When flags include MOUNT_CYGDRIVE, take this to mean that
 | 
						|
	we actually want to change the cygdrive prefix and flags
 | 
						|
	without actually mounting anything. */
 | 
						|
      res = mount_table->write_cygdrive_info_to_registry (posix_path, flags);
 | 
						|
      win32_path = NULL;
 | 
						|
    }
 | 
						|
  else if (!*win32_path)
 | 
						|
    set_errno (EINVAL);
 | 
						|
  else
 | 
						|
    res = mount_table->add_item (win32_path, posix_path, flags, true);
 | 
						|
 | 
						|
  syscall_printf ("%d = mount (%s, %s, %p)", res, win32_path, posix_path, flags);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
/* umount: The standard umount call only has a path parameter.  Since
 | 
						|
   it is not possible for this call to specify whether to remove the
 | 
						|
   mount from the user or global mount registry table, assume the user
 | 
						|
   table. */
 | 
						|
 | 
						|
extern "C" int
 | 
						|
umount (const char *path)
 | 
						|
{
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EFAULT))
 | 
						|
    return -1;
 | 
						|
  if (!*path)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  return cygwin_umount (path, 0);
 | 
						|
}
 | 
						|
 | 
						|
/* cygwin_umount: This is like umount but takes an additional flags
 | 
						|
   parameter that specifies whether to umount from the user or system-wide
 | 
						|
   registry area. */
 | 
						|
 | 
						|
extern "C" int
 | 
						|
cygwin_umount (const char *path, unsigned flags)
 | 
						|
{
 | 
						|
  int res = -1;
 | 
						|
 | 
						|
  if (flags & MOUNT_CYGDRIVE)
 | 
						|
    {
 | 
						|
      /* When flags include MOUNT_CYGDRIVE, take this to mean that we actually want
 | 
						|
	 to remove the cygdrive prefix and flags without actually unmounting
 | 
						|
	 anything. */
 | 
						|
      res = mount_table->remove_cygdrive_info_from_registry (path, flags);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      res = mount_table->del_item (path, flags, true);
 | 
						|
    }
 | 
						|
 | 
						|
  syscall_printf ("%d = cygwin_umount (%s, %d)", res,  path, flags);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
is_floppy (const char *dos)
 | 
						|
{
 | 
						|
  char dev[256];
 | 
						|
  if (!QueryDosDevice (dos, dev, 256))
 | 
						|
    return false;
 | 
						|
  return strncasematch (dev, "\\Device\\Floppy", 14)
 | 
						|
	 || strcasematch (dev, "A:");
 | 
						|
}
 | 
						|
 | 
						|
extern "C" FILE *
 | 
						|
setmntent (const char *filep, const char *)
 | 
						|
{
 | 
						|
  _my_tls.locals.iteration = 0;
 | 
						|
  _my_tls.locals.available_drives = GetLogicalDrives ();
 | 
						|
  /* Filter floppy drives on A: and B: */
 | 
						|
  if ((_my_tls.locals.available_drives & 1) && is_floppy ("A:"))
 | 
						|
    _my_tls.locals.available_drives &= ~1;
 | 
						|
  if ((_my_tls.locals.available_drives & 2) && is_floppy ("B:"))
 | 
						|
    _my_tls.locals.available_drives &= ~2;
 | 
						|
  return (FILE *) filep;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" struct mntent *
 | 
						|
getmntent (FILE *)
 | 
						|
{
 | 
						|
  return mount_table->getmntent (_my_tls.locals.iteration++);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
endmntent (FILE *)
 | 
						|
{
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
/********************** Symbolic Link Support **************************/
 | 
						|
 | 
						|
/* Read symlink from Extended Attribute */
 | 
						|
int
 | 
						|
get_symlink_ea (const char* frompath, char* buf, int buf_size)
 | 
						|
{
 | 
						|
  int res = NTReadEA (frompath, SYMLINK_EA_NAME, buf, buf_size);
 | 
						|
  if (res == 0)
 | 
						|
    debug_printf ("Cannot read symlink from EA");
 | 
						|
  return (res - 1);
 | 
						|
}
 | 
						|
 | 
						|
/* Save symlink to Extended Attribute */
 | 
						|
bool
 | 
						|
set_symlink_ea (const char* frompath, const char* topath)
 | 
						|
{
 | 
						|
  if (!NTWriteEA (frompath, SYMLINK_EA_NAME, topath, strlen (topath) + 1))
 | 
						|
    {
 | 
						|
      debug_printf ("Cannot save symlink in EA");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/* Create a symlink from FROMPATH to TOPATH. */
 | 
						|
 | 
						|
/* If TRUE create symlinks as Windows shortcuts, if false create symlinks
 | 
						|
   as normal files with magic number and system bit set. */
 | 
						|
bool allow_winsymlinks = true;
 | 
						|
 | 
						|
extern "C" int
 | 
						|
symlink (const char *oldpath, const char *newpath)
 | 
						|
{
 | 
						|
  return symlink_worker (oldpath, newpath, allow_winsymlinks, false);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
symlink_worker (const char *oldpath, const char *newpath, bool use_winsym,
 | 
						|
		bool isdevice)
 | 
						|
{
 | 
						|
  HANDLE h;
 | 
						|
  int res = -1;
 | 
						|
  path_conv win32_path, win32_oldpath;
 | 
						|
  char from[CYG_MAX_PATH + 5];
 | 
						|
  char cwd[CYG_MAX_PATH], *cp = NULL, c = 0;
 | 
						|
  char w32oldpath[CYG_MAX_PATH];
 | 
						|
  char reloldpath[CYG_MAX_PATH] = { 0 };
 | 
						|
  DWORD written;
 | 
						|
  SECURITY_ATTRIBUTES sa = sec_none_nih;
 | 
						|
  security_descriptor sd;
 | 
						|
 | 
						|
  /* POSIX says that empty 'newpath' is invalid input while empty
 | 
						|
     'oldpath' is valid -- it's symlink resolver job to verify if
 | 
						|
     symlink contents point to existing filesystem object */
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EFAULT))
 | 
						|
    goto done;
 | 
						|
  if (!*oldpath || !*newpath)
 | 
						|
    {
 | 
						|
      set_errno (ENOENT);
 | 
						|
      goto done;
 | 
						|
    }
 | 
						|
 | 
						|
  if (strlen (oldpath) >= CYG_MAX_PATH)
 | 
						|
    {
 | 
						|
      set_errno (ENAMETOOLONG);
 | 
						|
      goto done;
 | 
						|
    }
 | 
						|
 | 
						|
  win32_path.check (newpath, PC_SYM_NOFOLLOW);
 | 
						|
  if (use_winsym && !win32_path.exists ())
 | 
						|
    {
 | 
						|
      strcpy (from, newpath);
 | 
						|
      strcat (from, ".lnk");
 | 
						|
      win32_path.check (from, PC_SYM_NOFOLLOW);
 | 
						|
    }
 | 
						|
 | 
						|
  if (win32_path.error)
 | 
						|
    {
 | 
						|
      set_errno (win32_path.case_clash ? ECASECLASH : win32_path.error);
 | 
						|
      goto done;
 | 
						|
    }
 | 
						|
 | 
						|
  syscall_printf ("symlink (%s, %s)", oldpath, win32_path.get_win32 ());
 | 
						|
 | 
						|
  if (win32_path.is_auto_device ())
 | 
						|
    {
 | 
						|
      set_errno (EEXIST);
 | 
						|
      goto done;
 | 
						|
    }
 | 
						|
 | 
						|
  DWORD create_how;
 | 
						|
  if (!use_winsym)
 | 
						|
    create_how = CREATE_NEW;
 | 
						|
  else if (isdevice)
 | 
						|
    {
 | 
						|
      strcpy (w32oldpath, oldpath);
 | 
						|
      create_how = CREATE_ALWAYS;
 | 
						|
      SetFileAttributes (win32_path, FILE_ATTRIBUTE_NORMAL);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if (!isabspath (oldpath))
 | 
						|
	{
 | 
						|
	  getcwd (cwd, CYG_MAX_PATH);
 | 
						|
	  if ((cp = strrchr (from, '/')) || (cp = strrchr (from, '\\')))
 | 
						|
	    {
 | 
						|
	      c = *cp;
 | 
						|
	      *cp = '\0';
 | 
						|
	      chdir (from);
 | 
						|
	    }
 | 
						|
	  backslashify (oldpath, reloldpath, 0);
 | 
						|
	  /* Creating an ITEMIDLIST requires an absolute path.  So if we
 | 
						|
	     create a shortcut file, we create relative and absolute Win32
 | 
						|
	     paths, the first for the relpath field and the latter for the
 | 
						|
	     ITEMIDLIST field. */
 | 
						|
	  if (GetFileAttributes (reloldpath) == INVALID_FILE_ATTRIBUTES)
 | 
						|
	    {
 | 
						|
	      win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW);
 | 
						|
	      if (win32_oldpath.error != ENOENT)
 | 
						|
		strcpy (use_winsym ? reloldpath : w32oldpath, win32_oldpath);
 | 
						|
	    }
 | 
						|
	  else if (!use_winsym)
 | 
						|
	    strcpy (w32oldpath, reloldpath);
 | 
						|
	  if (use_winsym)
 | 
						|
	    {
 | 
						|
	      win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW);
 | 
						|
	      strcpy (w32oldpath, win32_oldpath);
 | 
						|
	    }
 | 
						|
	  if (cp)
 | 
						|
	    {
 | 
						|
	      *cp = c;
 | 
						|
	      chdir (cwd);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW);
 | 
						|
	  strcpy (w32oldpath, win32_oldpath);
 | 
						|
	}
 | 
						|
      create_how = CREATE_NEW;
 | 
						|
    }
 | 
						|
 | 
						|
  if (allow_ntsec && win32_path.has_acls ())
 | 
						|
    set_security_attribute (S_IFLNK | STD_RBITS | STD_WBITS,
 | 
						|
			    &sa, sd);
 | 
						|
 | 
						|
  h = CreateFile (win32_path, GENERIC_WRITE, 0, &sa, create_how,
 | 
						|
		  FILE_ATTRIBUTE_NORMAL, 0);
 | 
						|
  if (h == INVALID_HANDLE_VALUE)
 | 
						|
    __seterrno ();
 | 
						|
  else
 | 
						|
    {
 | 
						|
      bool success = false;
 | 
						|
 | 
						|
      if (use_winsym)
 | 
						|
	{
 | 
						|
	  /* A path of 240 chars with 120 one character directories in it
 | 
						|
	     can result in a 6K shortcut. */
 | 
						|
	  char *buf = (char *) alloca (8192);
 | 
						|
	  win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf;
 | 
						|
	  HRESULT hres;
 | 
						|
	  IShellFolder *psl;
 | 
						|
	  WCHAR wc_path[CYG_MAX_PATH];
 | 
						|
	  ITEMIDLIST *pidl = NULL, *p;
 | 
						|
	  unsigned short len;
 | 
						|
 | 
						|
	  memset (shortcut_header, 0, sizeof *shortcut_header);
 | 
						|
	  shortcut_header->size = sizeof *shortcut_header;
 | 
						|
	  shortcut_header->magic = GUID_shortcut;
 | 
						|
	  shortcut_header->flags = (WSH_FLAG_DESC | WSH_FLAG_RELPATH);
 | 
						|
	  shortcut_header->run = SW_NORMAL;
 | 
						|
	  cp = buf + sizeof (win_shortcut_hdr);
 | 
						|
	  /* Creating an IDLIST */
 | 
						|
	  hres = SHGetDesktopFolder (&psl);
 | 
						|
	  if (SUCCEEDED (hres))
 | 
						|
	    {
 | 
						|
	      MultiByteToWideChar (CP_ACP, 0, w32oldpath, -1, wc_path,
 | 
						|
				   CYG_MAX_PATH);
 | 
						|
	      hres = psl->ParseDisplayName (NULL, NULL, wc_path, NULL,
 | 
						|
					    &pidl, NULL);
 | 
						|
	      if (SUCCEEDED (hres))
 | 
						|
		{
 | 
						|
		  shortcut_header->flags |= WSH_FLAG_IDLIST;
 | 
						|
		  for (p = pidl; p->mkid.cb > 0;
 | 
						|
		       p = (ITEMIDLIST *)((char *) p + p->mkid.cb))
 | 
						|
		    ;
 | 
						|
		  len = (char *) p - (char *) pidl + 2;
 | 
						|
		  *(unsigned short *)cp = len;
 | 
						|
		  memcpy (cp += 2, pidl, len);
 | 
						|
		  cp += len;
 | 
						|
		  CoTaskMemFree (pidl);
 | 
						|
		}
 | 
						|
	      psl->Release ();
 | 
						|
	    }
 | 
						|
	  /* Creating a description */
 | 
						|
	  *(unsigned short *)cp = len = strlen (oldpath);
 | 
						|
	  memcpy (cp += 2, oldpath, len);
 | 
						|
	  cp += len;
 | 
						|
	  /* Creating a relpath */
 | 
						|
	  if (reloldpath[0])
 | 
						|
	    {
 | 
						|
	      *(unsigned short *)cp = len = strlen (reloldpath);
 | 
						|
	      memcpy (cp += 2, reloldpath, len);
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      *(unsigned short *)cp = len = strlen (w32oldpath);
 | 
						|
	      memcpy (cp += 2, w32oldpath, len);
 | 
						|
	    }
 | 
						|
	  cp += len;
 | 
						|
	  success = WriteFile (h, buf, cp - buf, &written, NULL)
 | 
						|
		    && written == (DWORD) (cp - buf);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* This is the old technique creating a symlink. */
 | 
						|
	  char buf[sizeof (SYMLINK_COOKIE) + CYG_MAX_PATH + 10];
 | 
						|
 | 
						|
	  __small_sprintf (buf, "%s%s", SYMLINK_COOKIE, oldpath);
 | 
						|
	  DWORD len = strlen (buf) + 1;
 | 
						|
 | 
						|
	  /* Note that the terminating nul is written.  */
 | 
						|
	  success = WriteFile (h, buf, len, &written, NULL)
 | 
						|
		    || written != len;
 | 
						|
 | 
						|
	}
 | 
						|
      if (success)
 | 
						|
	{
 | 
						|
	  CloseHandle (h);
 | 
						|
	  if (!allow_ntsec && allow_ntea)
 | 
						|
	    set_file_attribute (false, NULL, win32_path.get_win32 (),
 | 
						|
				S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO);
 | 
						|
 | 
						|
	  DWORD attr = use_winsym ? FILE_ATTRIBUTE_READONLY
 | 
						|
				  : FILE_ATTRIBUTE_SYSTEM;
 | 
						|
#ifdef HIDDEN_DOT_FILES
 | 
						|
	  cp = strrchr (win32_path, '\\');
 | 
						|
	  if ((cp && cp[1] == '.') || *win32_path == '.')
 | 
						|
	    attr |= FILE_ATTRIBUTE_HIDDEN;
 | 
						|
#endif
 | 
						|
	  SetFileAttributes (win32_path, attr);
 | 
						|
 | 
						|
	  if (!isdevice && win32_path.fs_has_ea ())
 | 
						|
	    set_symlink_ea (win32_path, oldpath);
 | 
						|
	  res = 0;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  __seterrno ();
 | 
						|
	  CloseHandle (h);
 | 
						|
	  DeleteFileA (win32_path.get_win32 ());
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
done:
 | 
						|
  syscall_printf ("%d = symlink_worker (%s, %s, %d, %d)", res, oldpath,
 | 
						|
		  newpath, use_winsym, isdevice);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
cmp_shortcut_header (win_shortcut_hdr *file_header)
 | 
						|
{
 | 
						|
  /* A Cygwin or U/Win shortcut only contains a description and a relpath.
 | 
						|
     Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
 | 
						|
     always set to SW_NORMAL. */
 | 
						|
  return file_header->size == sizeof (win_shortcut_hdr)
 | 
						|
      && !memcmp (&file_header->magic, &GUID_shortcut, sizeof GUID_shortcut)
 | 
						|
      && (file_header->flags & ~WSH_FLAG_IDLIST)
 | 
						|
	 == (WSH_FLAG_DESC | WSH_FLAG_RELPATH)
 | 
						|
      && file_header->run == SW_NORMAL;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
symlink_info::check_shortcut (const char *path, HANDLE h)
 | 
						|
{
 | 
						|
  win_shortcut_hdr *file_header;
 | 
						|
  char *buf, *cp;
 | 
						|
  unsigned short len;
 | 
						|
  int res = 0;
 | 
						|
  DWORD size, got = 0;
 | 
						|
 | 
						|
  /* Valid Cygwin & U/WIN shortcuts are R/O. */
 | 
						|
  if (!(fileattr & FILE_ATTRIBUTE_READONLY))
 | 
						|
    goto file_not_symlink;
 | 
						|
 | 
						|
  if ((size = GetFileSize (h, NULL)) > 8192) /* Not a Cygwin symlink. */
 | 
						|
    goto file_not_symlink;
 | 
						|
  buf = (char *) alloca (size);
 | 
						|
  if (!ReadFile (h, buf, size, &got, 0))
 | 
						|
    {
 | 
						|
      set_error (EIO);
 | 
						|
      goto close_it;
 | 
						|
    }
 | 
						|
  file_header = (win_shortcut_hdr *) buf;
 | 
						|
  if (got != size || !cmp_shortcut_header (file_header))
 | 
						|
    goto file_not_symlink;
 | 
						|
  cp = buf + sizeof (win_shortcut_hdr);
 | 
						|
  if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
 | 
						|
    cp += *(unsigned short *) cp + 2;
 | 
						|
  if ((len = *(unsigned short *) cp) == 0 || len >= CYG_MAX_PATH)
 | 
						|
    goto file_not_symlink;
 | 
						|
  strncpy (contents, cp += 2, len);
 | 
						|
  contents[len] = '\0';
 | 
						|
  res = len;
 | 
						|
  if (res) /* It's a symlink.  */
 | 
						|
    pflags = PATH_SYMLINK | PATH_LNK;
 | 
						|
  goto close_it;
 | 
						|
 | 
						|
file_not_symlink:
 | 
						|
  /* Not a symlink, see if executable.  */
 | 
						|
  if (!(pflags & PATH_ALL_EXEC) && has_exec_chars ((const char *) &file_header, got))
 | 
						|
    pflags |= PATH_EXEC;
 | 
						|
 | 
						|
close_it:
 | 
						|
  CloseHandle (h);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
symlink_info::check_sysfile (const char *path, HANDLE h)
 | 
						|
{
 | 
						|
  char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
 | 
						|
  DWORD got;
 | 
						|
  int res = 0;
 | 
						|
 | 
						|
  if (!ReadFile (h, cookie_buf, sizeof (cookie_buf), &got, 0))
 | 
						|
    {
 | 
						|
      debug_printf ("ReadFile1 failed");
 | 
						|
      set_error (EIO);
 | 
						|
    }
 | 
						|
  else if (got == sizeof (cookie_buf)
 | 
						|
	   && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0)
 | 
						|
    {
 | 
						|
      /* It's a symlink.  */
 | 
						|
      pflags = PATH_SYMLINK;
 | 
						|
 | 
						|
      res = ReadFile (h, contents, CYG_MAX_PATH, &got, 0);
 | 
						|
      if (!res)
 | 
						|
	{
 | 
						|
	  debug_printf ("ReadFile2 failed");
 | 
						|
	  set_error (EIO);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* Versions prior to b16 stored several trailing
 | 
						|
	     NULs with the path (to fill the path out to 1024
 | 
						|
	     chars).  Current versions only store one trailing
 | 
						|
	     NUL.  The length returned is the path without
 | 
						|
	     *any* trailing NULs.  We also have to handle (or
 | 
						|
	     at least not die from) corrupted paths.  */
 | 
						|
	  char *end;
 | 
						|
	  if ((end = (char *) memchr (contents, 0, got)) != NULL)
 | 
						|
	    res = end - contents;
 | 
						|
	  else
 | 
						|
	    res = got;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else if (got == sizeof (cookie_buf)
 | 
						|
	   && memcmp (cookie_buf, SOCKET_COOKIE, sizeof (cookie_buf)) == 0)
 | 
						|
    pflags |= PATH_SOCKET;
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* Not a symlink, see if executable.  */
 | 
						|
      if (pflags & PATH_ALL_EXEC)
 | 
						|
	/* Nothing to do */;
 | 
						|
      else if (has_exec_chars (cookie_buf, got))
 | 
						|
	pflags |= PATH_EXEC;
 | 
						|
      else
 | 
						|
	pflags |= PATH_NOTEXEC;
 | 
						|
      }
 | 
						|
  syscall_printf ("%d = symlink.check_sysfile (%s, %s) (%p)",
 | 
						|
		  res, path, contents, pflags);
 | 
						|
 | 
						|
  CloseHandle (h);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
enum
 | 
						|
{
 | 
						|
  SCAN_BEG,
 | 
						|
  SCAN_LNK,
 | 
						|
  SCAN_HASLNK,
 | 
						|
  SCAN_JUSTCHECK,
 | 
						|
  SCAN_APPENDLNK,
 | 
						|
  SCAN_EXTRALNK,
 | 
						|
  SCAN_DONE,
 | 
						|
};
 | 
						|
 | 
						|
class suffix_scan
 | 
						|
{
 | 
						|
  const suffix_info *suffixes, *suffixes_start;
 | 
						|
  int nextstate;
 | 
						|
  char *eopath;
 | 
						|
public:
 | 
						|
  const char *path;
 | 
						|
  char *has (const char *, const suffix_info *);
 | 
						|
  int next ();
 | 
						|
  int lnk_match () {return nextstate >= SCAN_EXTRALNK;}
 | 
						|
};
 | 
						|
 | 
						|
char *
 | 
						|
suffix_scan::has (const char *in_path, const suffix_info *in_suffixes)
 | 
						|
{
 | 
						|
  nextstate = SCAN_BEG;
 | 
						|
  suffixes = suffixes_start = in_suffixes;
 | 
						|
 | 
						|
  char *ext_here = strrchr (in_path, '.');
 | 
						|
  path = in_path;
 | 
						|
  eopath = strchr (path, '\0');
 | 
						|
 | 
						|
  if (!ext_here)
 | 
						|
    goto noext;
 | 
						|
 | 
						|
  if (suffixes)
 | 
						|
    {
 | 
						|
      /* Check if the extension matches a known extension */
 | 
						|
      for (const suffix_info *ex = in_suffixes; ex->name != NULL; ex++)
 | 
						|
	if (strcasematch (ext_here, ex->name))
 | 
						|
	  {
 | 
						|
	    nextstate = SCAN_JUSTCHECK;
 | 
						|
	    suffixes = NULL;	/* Has an extension so don't scan for one. */
 | 
						|
	    goto done;
 | 
						|
	  }
 | 
						|
    }
 | 
						|
 | 
						|
  /* Didn't match.  Use last resort -- .lnk. */
 | 
						|
  if (strcasematch (ext_here, ".lnk"))
 | 
						|
    {
 | 
						|
      nextstate = SCAN_HASLNK;
 | 
						|
      suffixes = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
 noext:
 | 
						|
  ext_here = eopath;
 | 
						|
 | 
						|
 done:
 | 
						|
  return ext_here;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
suffix_scan::next ()
 | 
						|
{
 | 
						|
  for (;;)
 | 
						|
    {
 | 
						|
      if (!suffixes)
 | 
						|
	switch (nextstate)
 | 
						|
	  {
 | 
						|
	  case SCAN_BEG:
 | 
						|
	    suffixes = suffixes_start;
 | 
						|
	    if (!suffixes)
 | 
						|
	      {
 | 
						|
		nextstate = SCAN_LNK;
 | 
						|
		return 1;
 | 
						|
	      }
 | 
						|
	    if (!*suffixes->name)
 | 
						|
	      suffixes++;
 | 
						|
	    nextstate = SCAN_EXTRALNK;
 | 
						|
	    /* fall through to suffix checking below */
 | 
						|
	    break;
 | 
						|
	  case SCAN_HASLNK:
 | 
						|
	    nextstate = SCAN_EXTRALNK;	/* Skip SCAN_BEG */
 | 
						|
	    return 1;
 | 
						|
	  case SCAN_LNK:
 | 
						|
	  case SCAN_EXTRALNK:
 | 
						|
	    strcpy (eopath, ".lnk");
 | 
						|
	    nextstate = SCAN_DONE;
 | 
						|
	    return 1;
 | 
						|
	  case SCAN_JUSTCHECK:
 | 
						|
	    nextstate = SCAN_APPENDLNK;
 | 
						|
	    return 1;
 | 
						|
	  case SCAN_APPENDLNK:
 | 
						|
	    strcat (eopath, ".lnk");
 | 
						|
	    nextstate = SCAN_DONE;
 | 
						|
	    return 1;
 | 
						|
	  default:
 | 
						|
	    *eopath = '\0';
 | 
						|
	    return 0;
 | 
						|
	  }
 | 
						|
 | 
						|
      while (suffixes && suffixes->name)
 | 
						|
	if (!suffixes->addon)
 | 
						|
	  suffixes++;
 | 
						|
	else
 | 
						|
	  {
 | 
						|
	    strcpy (eopath, suffixes->name);
 | 
						|
	    if (nextstate == SCAN_EXTRALNK)
 | 
						|
	      strcat (eopath, ".lnk");
 | 
						|
	    suffixes++;
 | 
						|
	    return 1;
 | 
						|
	  }
 | 
						|
      suffixes = NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
symlink_info::set_error (int in_errno)
 | 
						|
{
 | 
						|
  bool res;
 | 
						|
  if (!(pflags & PATH_NO_ACCESS_CHECK) || in_errno == ENAMETOOLONG || in_errno == EIO)
 | 
						|
    {
 | 
						|
      error = in_errno;
 | 
						|
      res = true;
 | 
						|
    }
 | 
						|
  else if (in_errno == ENOENT)
 | 
						|
    res = true;
 | 
						|
  else
 | 
						|
    {
 | 
						|
      fileattr = FILE_ATTRIBUTE_NORMAL;
 | 
						|
      res = false;
 | 
						|
    }
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
symlink_info::parse_device (const char *contents)
 | 
						|
{
 | 
						|
  char *endptr;
 | 
						|
  _major_t mymajor;
 | 
						|
  _major_t myminor;
 | 
						|
  _mode_t mymode;
 | 
						|
 | 
						|
  mymajor = strtol (contents += 2, &endptr, 16);
 | 
						|
  if (endptr == contents)
 | 
						|
    return isdevice = false;
 | 
						|
 | 
						|
  contents = endptr;
 | 
						|
  myminor = strtol (++contents, &endptr, 16);
 | 
						|
  if (endptr == contents)
 | 
						|
    return isdevice = false;
 | 
						|
 | 
						|
  contents = endptr;
 | 
						|
  mymode = strtol (++contents, &endptr, 16);
 | 
						|
  if (endptr == contents)
 | 
						|
    return isdevice = false;
 | 
						|
 | 
						|
  if ((mymode & S_IFMT) == S_IFIFO)
 | 
						|
    {
 | 
						|
      mymajor = _major (FH_FIFO);
 | 
						|
      myminor = _minor (FH_FIFO);
 | 
						|
    }
 | 
						|
 | 
						|
  major = mymajor;
 | 
						|
  minor = myminor;
 | 
						|
  mode = mymode;
 | 
						|
  return isdevice = true;
 | 
						|
}
 | 
						|
 | 
						|
/* Check if PATH is a symlink.  PATH must be a valid Win32 path name.
 | 
						|
 | 
						|
   If PATH is a symlink, put the value of the symlink--the file to
 | 
						|
   which it points--into BUF.  The value stored in BUF is not
 | 
						|
   necessarily null terminated.  BUFLEN is the length of BUF; only up
 | 
						|
   to BUFLEN characters will be stored in BUF.  BUF may be NULL, in
 | 
						|
   which case nothing will be stored.
 | 
						|
 | 
						|
   Set *SYML if PATH is a symlink.
 | 
						|
 | 
						|
   Set *EXEC if PATH appears to be executable.  This is an efficiency
 | 
						|
   hack because we sometimes have to open the file anyhow.  *EXEC will
 | 
						|
   not be set for every executable file.
 | 
						|
 | 
						|
   Return -1 on error, 0 if PATH is not a symlink, or the length
 | 
						|
   stored into BUF if PATH is a symlink.  */
 | 
						|
 | 
						|
int
 | 
						|
symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt)
 | 
						|
{
 | 
						|
  HANDLE h;
 | 
						|
  int res = 0;
 | 
						|
  suffix_scan suffix;
 | 
						|
  contents[0] = '\0';
 | 
						|
 | 
						|
  issymlink = true;
 | 
						|
  isdevice = false;
 | 
						|
  ext_here = suffix.has (path, suffixes);
 | 
						|
  extn = ext_here - path;
 | 
						|
  major = 0;
 | 
						|
  minor = 0;
 | 
						|
  mode = 0;
 | 
						|
 | 
						|
  pflags &= ~(PATH_SYMLINK | PATH_LNK);
 | 
						|
  unsigned pflags_or = pflags & PATH_NO_ACCESS_CHECK;
 | 
						|
 | 
						|
  case_clash = false;
 | 
						|
 | 
						|
  while (suffix.next ())
 | 
						|
    {
 | 
						|
      error = 0;
 | 
						|
      fileattr = GetFileAttributes (suffix.path);
 | 
						|
      if (fileattr == INVALID_FILE_ATTRIBUTES)
 | 
						|
	{
 | 
						|
	  /* The GetFileAttributes call can fail for reasons that don't
 | 
						|
	     matter, so we just return 0.  For example, getting the
 | 
						|
	     attributes of \\HOST will typically fail.  */
 | 
						|
	  debug_printf ("GetFileAttributes (%s) failed", suffix.path);
 | 
						|
 | 
						|
	  /* The above comment is not *quite* right.  When calling
 | 
						|
	     GetFileAttributes for a non-existant file an a Win9x share,
 | 
						|
	     GetLastError returns ERROR_INVALID_FUNCTION.  Go figure!
 | 
						|
	     Also, GetFileAttributes fails with ERROR_SHARING_VIOLATION
 | 
						|
	     if the file is locked exclusively by another process.
 | 
						|
	     If we don't special handle this here, the file is accidentally
 | 
						|
	     treated as non-existant. */
 | 
						|
	  DWORD win_error = GetLastError ();
 | 
						|
	  if (win_error == ERROR_INVALID_FUNCTION)
 | 
						|
	    win_error = ERROR_FILE_NOT_FOUND;
 | 
						|
	  else if (win_error == ERROR_SHARING_VIOLATION)
 | 
						|
	    {
 | 
						|
	      ext_tacked_on = !!*ext_here;
 | 
						|
	      fileattr = 0;
 | 
						|
	      goto file_not_symlink;
 | 
						|
	    }
 | 
						|
	  if (set_error (geterrno_from_win_error (win_error, EACCES)))
 | 
						|
	    continue;
 | 
						|
	}
 | 
						|
 | 
						|
      ext_tacked_on = !!*ext_here;
 | 
						|
 | 
						|
      if (pcheck_case != PCHECK_RELAXED && !case_check (path)
 | 
						|
	  || (opt & PC_SYM_IGNORE))
 | 
						|
	goto file_not_symlink;
 | 
						|
 | 
						|
      int sym_check;
 | 
						|
 | 
						|
      sym_check = 0;
 | 
						|
 | 
						|
      if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
 | 
						|
	goto file_not_symlink;
 | 
						|
 | 
						|
      /* Windows shortcuts are treated as symlinks. */
 | 
						|
      if (suffix.lnk_match ())
 | 
						|
	sym_check = 1;
 | 
						|
 | 
						|
      /* This is the old Cygwin method creating symlinks: */
 | 
						|
      /* A symlink will have the `system' file attribute. */
 | 
						|
      /* Only files can be symlinks (which can be symlinks to directories). */
 | 
						|
      if (fileattr & FILE_ATTRIBUTE_SYSTEM)
 | 
						|
	sym_check = 2;
 | 
						|
 | 
						|
      if (!sym_check)
 | 
						|
	goto file_not_symlink;
 | 
						|
 | 
						|
      if (sym_check > 0 && opt & PC_CHECK_EA &&
 | 
						|
	  (res = get_symlink_ea (suffix.path, contents, sizeof (contents))) > 0)
 | 
						|
	{
 | 
						|
	  pflags = PATH_SYMLINK | pflags_or;
 | 
						|
	  if (sym_check == 1)
 | 
						|
	    pflags |= PATH_LNK;
 | 
						|
	  debug_printf ("Got symlink from EA: %s", contents);
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
 | 
						|
      /* Open the file.  */
 | 
						|
 | 
						|
      h = CreateFile (suffix.path, GENERIC_READ, FILE_SHARE_READ,
 | 
						|
		      &sec_none_nih, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
 | 
						|
      res = -1;
 | 
						|
      if (h == INVALID_HANDLE_VALUE)
 | 
						|
	goto file_not_symlink;
 | 
						|
 | 
						|
      /* FIXME: if symlink isn't present in EA, but EAs are supported,
 | 
						|
	 should we write it there?  */
 | 
						|
      switch (sym_check)
 | 
						|
	{
 | 
						|
	case 1:
 | 
						|
	  res = check_shortcut (suffix.path, h);
 | 
						|
	  if (!res)
 | 
						|
	    /* check more below */;
 | 
						|
	  else if (contents[0] == ':' && contents[1] == '\\' && parse_device (contents))
 | 
						|
	    goto file_not_symlink;
 | 
						|
	  else
 | 
						|
	    break;
 | 
						|
	  /* If searching for `foo' and then finding a `foo.lnk' which is
 | 
						|
	     no shortcut, return the same as if file not found. */
 | 
						|
	  if (!suffix.lnk_match () || !ext_tacked_on)
 | 
						|
	    goto file_not_symlink;
 | 
						|
 | 
						|
	  fileattr = INVALID_FILE_ATTRIBUTES;
 | 
						|
	  continue;		/* in case we're going to tack *another* .lnk on this filename. */
 | 
						|
	case 2:
 | 
						|
	  res = check_sysfile (suffix.path, h);
 | 
						|
	  if (!res)
 | 
						|
	    goto file_not_symlink;
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      break;
 | 
						|
 | 
						|
    file_not_symlink:
 | 
						|
      issymlink = false;
 | 
						|
      syscall_printf ("%s", isdevice ? "is a device" : "not a symlink");
 | 
						|
      res = 0;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  syscall_printf ("%d = symlink.check (%s, %p) (%p)",
 | 
						|
		  res, suffix.path, contents, pflags);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
/* "path" is the path in a virtual symlink.  Set a symlink_info struct from
 | 
						|
   that and proceed with further path checking afterwards. */
 | 
						|
int
 | 
						|
symlink_info::set (char *path)
 | 
						|
{
 | 
						|
  strcpy (contents, path);
 | 
						|
  pflags = PATH_SYMLINK;
 | 
						|
  fileattr = FILE_ATTRIBUTE_NORMAL;
 | 
						|
  error = 0;
 | 
						|
  issymlink = true;
 | 
						|
  isdevice = false;
 | 
						|
  ext_tacked_on = case_clash = false;
 | 
						|
  ext_here = NULL;
 | 
						|
  extn = major = minor = mode = 0;
 | 
						|
  return strlen (path);
 | 
						|
}
 | 
						|
 | 
						|
/* Check the correct case of the last path component (given in DOS style).
 | 
						|
   Adjust the case in this->path if pcheck_case == PCHECK_ADJUST or return
 | 
						|
   false if pcheck_case == PCHECK_STRICT.
 | 
						|
   Dont't call if pcheck_case == PCHECK_RELAXED.
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
symlink_info::case_check (char *path)
 | 
						|
{
 | 
						|
  WIN32_FIND_DATA data;
 | 
						|
  HANDLE h;
 | 
						|
  char *c;
 | 
						|
 | 
						|
  /* Set a pointer to the beginning of the last component. */
 | 
						|
  if (!(c = strrchr (path, '\\')))
 | 
						|
    c = path;
 | 
						|
  else
 | 
						|
    ++c;
 | 
						|
 | 
						|
  if ((h = FindFirstFile (path, &data))
 | 
						|
      != INVALID_HANDLE_VALUE)
 | 
						|
    {
 | 
						|
      FindClose (h);
 | 
						|
 | 
						|
      /* If that part of the component exists, check the case. */
 | 
						|
      if (strncmp (c, data.cFileName, strlen (data.cFileName)))
 | 
						|
	{
 | 
						|
	  case_clash = true;
 | 
						|
 | 
						|
	  /* If check is set to STRICT, a wrong case results
 | 
						|
	     in returning a ENOENT. */
 | 
						|
	  if (pcheck_case == PCHECK_STRICT)
 | 
						|
	    return false;
 | 
						|
 | 
						|
	  /* PCHECK_ADJUST adjusts the case in the incoming
 | 
						|
	     path which points to the path in *this. */
 | 
						|
	  strcpy (c, data.cFileName);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/* readlink system call */
 | 
						|
 | 
						|
extern "C" int
 | 
						|
readlink (const char *path, char *buf, int buflen)
 | 
						|
{
 | 
						|
  extern suffix_info stat_suffixes[];
 | 
						|
 | 
						|
  if (buflen < 0)
 | 
						|
    {
 | 
						|
      set_errno (ENAMETOOLONG);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  path_conv pathbuf (path, PC_SYM_CONTENTS, stat_suffixes);
 | 
						|
 | 
						|
  if (pathbuf.error)
 | 
						|
    {
 | 
						|
      set_errno (pathbuf.error);
 | 
						|
      syscall_printf ("-1 = readlink (%s, %p, %d)", path, buf, buflen);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!pathbuf.exists ())
 | 
						|
    {
 | 
						|
      set_errno (ENOENT);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!pathbuf.issymlink ())
 | 
						|
    {
 | 
						|
      if (pathbuf.exists ())
 | 
						|
	set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  int len = min (buflen, (int) strlen (pathbuf.get_win32 ()));
 | 
						|
  memcpy (buf, pathbuf.get_win32 (), len);
 | 
						|
 | 
						|
  /* errno set by symlink.check if error */
 | 
						|
  return len;
 | 
						|
}
 | 
						|
 | 
						|
/* Some programs rely on st_dev/st_ino being unique for each file.
 | 
						|
   Hash the path name and hope for the best.  The hash arg is not
 | 
						|
   always initialized to zero since readdir needs to compute the
 | 
						|
   dirent ino_t based on a combination of the hash of the directory
 | 
						|
   done during the opendir call and the hash or the filename within
 | 
						|
   the directory.  FIXME: Not bullet-proof. */
 | 
						|
/* Cygwin internal */
 | 
						|
 | 
						|
__ino64_t __stdcall
 | 
						|
hash_path_name (__ino64_t hash, const char *name)
 | 
						|
{
 | 
						|
  if (!*name)
 | 
						|
    return hash;
 | 
						|
 | 
						|
  /* Perform some initial permutations on the pathname if this is
 | 
						|
     not "seeded" */
 | 
						|
  if (!hash)
 | 
						|
    {
 | 
						|
      /* Simplistic handling of drives.  If there is a drive specified,
 | 
						|
	 make sure that the initial letter is upper case.  If there is
 | 
						|
	 no \ after the ':' assume access through the root directory
 | 
						|
	 of that drive.
 | 
						|
	 FIXME:  Should really honor MS-Windows convention of using
 | 
						|
	 the environment to track current directory on various drives. */
 | 
						|
      if (name[1] == ':')
 | 
						|
	{
 | 
						|
	  char *nn, *newname = (char *) alloca (strlen (name) + 2);
 | 
						|
	  nn = newname;
 | 
						|
	  *nn = isupper (*name) ? cyg_tolower (*name) : *name;
 | 
						|
	  *++nn = ':';
 | 
						|
	  name += 2;
 | 
						|
	  if (*name != '\\')
 | 
						|
	    *++nn = '\\';
 | 
						|
	  strcpy (++nn, name);
 | 
						|
	  name = newname;
 | 
						|
	  goto hashit;
 | 
						|
	}
 | 
						|
 | 
						|
      /* Fill out the hashed path name with the current working directory if
 | 
						|
	 this is not an absolute path and there is no pre-specified hash value.
 | 
						|
	 Otherwise the inodes same will differ depending on whether a file is
 | 
						|
	 referenced with an absolute value or relatively. */
 | 
						|
 | 
						|
      if (!hash && !isabspath (name))
 | 
						|
	{
 | 
						|
	  hash = cygheap->cwd.get_hash ();
 | 
						|
	  if (name[0] == '.' && name[1] == '\0')
 | 
						|
	    return hash;
 | 
						|
	  hash = '\\' + (hash << 6) + (hash << 16) - hash;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
hashit:
 | 
						|
  /* Build up hash. Name is already normalized */
 | 
						|
  do
 | 
						|
    {
 | 
						|
      int ch = cyg_tolower (*name);
 | 
						|
      hash = ch + (hash << 6) + (hash << 16) - hash;
 | 
						|
    }
 | 
						|
  while (*++name != '\0');
 | 
						|
  return hash;
 | 
						|
}
 | 
						|
 | 
						|
char *
 | 
						|
getcwd (char *buf, size_t ulen)
 | 
						|
{
 | 
						|
  char* res = NULL;
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EFAULT))
 | 
						|
      /* errno set */;
 | 
						|
  else if (ulen == 0 && buf)
 | 
						|
    set_errno (EINVAL);
 | 
						|
  else
 | 
						|
    res = cygheap->cwd.get (buf, 1, 1, ulen);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
/* getwd: standards? */
 | 
						|
extern "C" char *
 | 
						|
getwd (char *buf)
 | 
						|
{
 | 
						|
  return getcwd (buf, CYG_MAX_PATH);
 | 
						|
}
 | 
						|
 | 
						|
/* chdir: POSIX 5.2.1.1 */
 | 
						|
extern "C" int
 | 
						|
chdir (const char *in_dir)
 | 
						|
{
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EFAULT))
 | 
						|
    return -1;
 | 
						|
  if (!*in_dir)
 | 
						|
    {
 | 
						|
      set_errno (ENOENT);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  syscall_printf ("dir '%s'", in_dir);
 | 
						|
 | 
						|
  /* Convert path.  First argument ensures that we don't check for NULL/empty/invalid
 | 
						|
     again. */
 | 
						|
  path_conv path (PC_NONULLEMPTY, in_dir, PC_SYM_FOLLOW | PC_POSIX);
 | 
						|
  if (path.error)
 | 
						|
    {
 | 
						|
      set_errno (path.error);
 | 
						|
      syscall_printf ("-1 = chdir (%s)", in_dir);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  int res = -1;
 | 
						|
  bool doit = false;
 | 
						|
  const char *native_dir = path, *posix_cwd = NULL;
 | 
						|
  int devn = path.get_devn ();
 | 
						|
  if (!isvirtual_dev (devn))
 | 
						|
    {
 | 
						|
      /* Check to see if path translates to something like C:.
 | 
						|
	 If it does, append a \ to the native directory specification to
 | 
						|
	 defeat the Windows 95 (i.e. MS-DOS) tendency of returning to
 | 
						|
	 the last directory visited on the given drive. */
 | 
						|
      if (isdrive (native_dir) && !native_dir[2])
 | 
						|
	{
 | 
						|
	  path.get_win32 ()[2] = '\\';
 | 
						|
	  path.get_win32 ()[3] = '\0';
 | 
						|
	}
 | 
						|
      /* The sequence chdir("xx"); chdir(".."); must be a noop if xx
 | 
						|
	 is not a symlink. This is exploited by find.exe.
 | 
						|
	 The posix_cwd is just path.normalized_path.
 | 
						|
	 In other cases we let cwd.set obtain the Posix path through
 | 
						|
	 the mount table. */
 | 
						|
      if (!isdrive(path.normalized_path))
 | 
						|
	posix_cwd = path.normalized_path;
 | 
						|
      res = 0;
 | 
						|
      doit = true;
 | 
						|
    }
 | 
						|
  else if (!path.exists ())
 | 
						|
    set_errno (ENOENT);
 | 
						|
  else if (!path.isdir ())
 | 
						|
    set_errno (ENOTDIR);
 | 
						|
  else
 | 
						|
   {
 | 
						|
     posix_cwd = path.normalized_path;
 | 
						|
     res = 0;
 | 
						|
   }
 | 
						|
 | 
						|
  if (!res)
 | 
						|
    res = cygheap->cwd.set (native_dir, posix_cwd, doit);
 | 
						|
 | 
						|
  /* Note that we're accessing cwd.posix without a lock here.  I didn't think
 | 
						|
     it was worth locking just for strace. */
 | 
						|
  syscall_printf ("%d = chdir() cygheap->cwd.posix '%s' native '%s'", res,
 | 
						|
		  cygheap->cwd.posix, native_dir);
 | 
						|
  MALLOC_CHECK;
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
fchdir (int fd)
 | 
						|
{
 | 
						|
  int res;
 | 
						|
  cygheap_fdget cfd (fd);
 | 
						|
  if (cfd >= 0)
 | 
						|
    res = chdir (cfd->get_name ());
 | 
						|
  else
 | 
						|
    res = -1;
 | 
						|
 | 
						|
  syscall_printf ("%d = fchdir (%d)", res, fd);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
/******************** Exported Path Routines *********************/
 | 
						|
 | 
						|
/* Cover functions to the path conversion routines.
 | 
						|
   These are exported to the world as cygwin_foo by cygwin.din.  */
 | 
						|
 | 
						|
#define return_with_errno(x) \
 | 
						|
  do {\
 | 
						|
    int err = (x);\
 | 
						|
    if (!err)\
 | 
						|
     return 0;\
 | 
						|
    set_errno (err);\
 | 
						|
    return -1;\
 | 
						|
  } while (0)
 | 
						|
 | 
						|
extern "C" int
 | 
						|
cygwin_conv_to_win32_path (const char *path, char *win32_path)
 | 
						|
{
 | 
						|
  path_conv p (path, PC_SYM_FOLLOW | PC_NO_ACCESS_CHECK | PC_NOFULL);
 | 
						|
  if (p.error)
 | 
						|
    {
 | 
						|
      win32_path[0] = '\0';
 | 
						|
      set_errno (p.error);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  strcpy (win32_path, p);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
cygwin_conv_to_full_win32_path (const char *path, char *win32_path)
 | 
						|
{
 | 
						|
  path_conv p (path, PC_SYM_FOLLOW | PC_NO_ACCESS_CHECK);
 | 
						|
  if (p.error)
 | 
						|
    {
 | 
						|
      win32_path[0] = '\0';
 | 
						|
      set_errno (p.error);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  strcpy (win32_path, p);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* This is exported to the world as cygwin_foo by cygwin.din.  */
 | 
						|
 | 
						|
extern "C" int
 | 
						|
cygwin_conv_to_posix_path (const char *path, char *posix_path)
 | 
						|
{
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EFAULT))
 | 
						|
    return -1;
 | 
						|
  if (!*path)
 | 
						|
    {
 | 
						|
      set_errno (ENOENT);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  return_with_errno (mount_table->conv_to_posix_path (path, posix_path, 1));
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
cygwin_conv_to_full_posix_path (const char *path, char *posix_path)
 | 
						|
{
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EFAULT))
 | 
						|
    return -1;
 | 
						|
  if (!*path)
 | 
						|
    {
 | 
						|
      set_errno (ENOENT);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  return_with_errno (mount_table->conv_to_posix_path (path, posix_path, 0));
 | 
						|
}
 | 
						|
 | 
						|
/* The realpath function is supported on some UNIX systems.  */
 | 
						|
 | 
						|
extern "C" char *
 | 
						|
realpath (const char *path, char *resolved)
 | 
						|
{
 | 
						|
  extern suffix_info stat_suffixes[];
 | 
						|
 | 
						|
  /* Make sure the right errno is returned if path is NULL. */
 | 
						|
  if (!path)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Guard reading from a potentially invalid path and writing to a
 | 
						|
     potentially invalid resolved. */
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EFAULT))
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  char *tpath;
 | 
						|
  if (isdrive (path))
 | 
						|
    {
 | 
						|
      tpath = (char *) alloca (strlen (path)
 | 
						|
			       + strlen (mount_table->cygdrive)
 | 
						|
			       + 1);
 | 
						|
      mount_table->cygdrive_posix_path (path, tpath, 0);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    tpath = (char *) path;
 | 
						|
 | 
						|
  path_conv real_path (tpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
 | 
						|
 | 
						|
 | 
						|
  /* Linux has this funny non-standard extension.  If "resolved" is NULL,
 | 
						|
     realpath mallocs the space by itself and returns it to the application.
 | 
						|
     The application is responsible for calling free() then.  This extension
 | 
						|
     is backed by POSIX, which allows implementation-defined behaviour if
 | 
						|
     "resolved" is NULL.  That's good enough for us to do the same here. */
 | 
						|
 | 
						|
  if (!real_path.error && real_path.exists ())
 | 
						|
    {
 | 
						|
      if (!resolved)
 | 
						|
	{
 | 
						|
          resolved = (char *) malloc (strlen (real_path.normalized_path) + 1);
 | 
						|
          if (!resolved)
 | 
						|
	    return NULL;
 | 
						|
        }
 | 
						|
      return strcpy (resolved, real_path.normalized_path);
 | 
						|
    }
 | 
						|
 | 
						|
  /* FIXME: on error, we are supposed to put the name of the path
 | 
						|
     component which could not be resolved into RESOLVED.  */
 | 
						|
  if (resolved)
 | 
						|
    resolved[0] = '\0';
 | 
						|
  set_errno (real_path.error ?: ENOENT);
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* Return non-zero if path is a POSIX path list.
 | 
						|
   This is exported to the world as cygwin_foo by cygwin.din.
 | 
						|
 | 
						|
DOCTOOL-START
 | 
						|
<sect1 id="add-func-cygwin-posix-path-list-p">
 | 
						|
  <para>Rather than use a mode to say what the "proper" path list
 | 
						|
  format is, we allow any, and give apps the tools they need to
 | 
						|
  convert between the two.  If a ';' is present in the path list it's
 | 
						|
  a Win32 path list.  Otherwise, if the first path begins with
 | 
						|
  [letter]: (in which case it can be the only element since if it
 | 
						|
  wasn't a ';' would be present) it's a Win32 path list.  Otherwise,
 | 
						|
  it's a POSIX path list.</para>
 | 
						|
</sect1>
 | 
						|
DOCTOOL-END
 | 
						|
  */
 | 
						|
 | 
						|
extern "C" int
 | 
						|
cygwin_posix_path_list_p (const char *path)
 | 
						|
{
 | 
						|
  int posix_p = !(strchr (path, ';') || isdrive (path));
 | 
						|
  return posix_p;
 | 
						|
}
 | 
						|
 | 
						|
/* These are used for apps that need to convert env vars like PATH back and
 | 
						|
   forth.  The conversion is a two step process.  First, an upper bound on the
 | 
						|
   size of the buffer needed is computed.  Then the conversion is done.  This
 | 
						|
   allows the caller to use alloca if it wants.  */
 | 
						|
 | 
						|
static int
 | 
						|
conv_path_list_buf_size (const char *path_list, bool to_posix)
 | 
						|
{
 | 
						|
  int i, num_elms, max_mount_path_len, size;
 | 
						|
  const char *p;
 | 
						|
 | 
						|
  path_conv pc(".", PC_POSIX);
 | 
						|
  /* The theory is that an upper bound is
 | 
						|
     current_size + (num_elms * max_mount_path_len)  */
 | 
						|
 | 
						|
  unsigned nrel;
 | 
						|
  char delim = to_posix ? ';' : ':';
 | 
						|
  for (p = path_list, num_elms = nrel = 0; p; num_elms++)
 | 
						|
    {
 | 
						|
      if (!isabspath (p))
 | 
						|
	nrel++;
 | 
						|
      p = strchr (++p, delim);
 | 
						|
    }
 | 
						|
 | 
						|
  /* 7: strlen ("//c") + slop, a conservative initial value */
 | 
						|
  for (max_mount_path_len = sizeof ("/cygdrive/X"), i = 0;
 | 
						|
       i < mount_table->nmounts; i++)
 | 
						|
    {
 | 
						|
      int mount_len = (to_posix
 | 
						|
		       ? mount_table->mount[i].posix_pathlen
 | 
						|
		       : mount_table->mount[i].native_pathlen);
 | 
						|
      if (max_mount_path_len < mount_len)
 | 
						|
	max_mount_path_len = mount_len;
 | 
						|
    }
 | 
						|
 | 
						|
  /* 100: slop */
 | 
						|
  size = strlen (path_list)
 | 
						|
    + (num_elms * max_mount_path_len)
 | 
						|
    + (nrel * strlen (to_posix ? pc.normalized_path : pc.get_win32 ()))
 | 
						|
    + 100;
 | 
						|
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
extern "C" int
 | 
						|
cygwin_win32_to_posix_path_list_buf_size (const char *path_list)
 | 
						|
{
 | 
						|
  return conv_path_list_buf_size (path_list, true);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
cygwin_posix_to_win32_path_list_buf_size (const char *path_list)
 | 
						|
{
 | 
						|
  return conv_path_list_buf_size (path_list, false);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
cygwin_win32_to_posix_path_list (const char *win32, char *posix)
 | 
						|
{
 | 
						|
  return_with_errno (conv_path_list (win32, posix, 1));
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
cygwin_posix_to_win32_path_list (const char *posix, char *win32)
 | 
						|
{
 | 
						|
  return_with_errno (conv_path_list (posix, win32, 0));
 | 
						|
}
 | 
						|
 | 
						|
/* cygwin_split_path: Split a path into directory and file name parts.
 | 
						|
   Buffers DIR and FILE are assumed to be big enough.
 | 
						|
 | 
						|
   Examples (path -> `dir' / `file'):
 | 
						|
   / -> `/' / `'
 | 
						|
   "" -> `.' / `'
 | 
						|
   . -> `.' / `.' (FIXME: should this be `.' / `'?)
 | 
						|
   .. -> `.' / `..' (FIXME: should this be `..' / `'?)
 | 
						|
   foo -> `.' / `foo'
 | 
						|
   foo/bar -> `foo' / `bar'
 | 
						|
   foo/bar/ -> `foo' / `bar'
 | 
						|
   /foo -> `/' / `foo'
 | 
						|
   /foo/bar -> `/foo' / `bar'
 | 
						|
   c: -> `c:/' / `'
 | 
						|
   c:/ -> `c:/' / `'
 | 
						|
   c:foo -> `c:/' / `foo'
 | 
						|
   c:/foo -> `c:/' / `foo'
 | 
						|
 */
 | 
						|
 | 
						|
extern "C" void
 | 
						|
cygwin_split_path (const char *path, char *dir, char *file)
 | 
						|
{
 | 
						|
  int dir_started_p = 0;
 | 
						|
 | 
						|
  /* Deal with drives.
 | 
						|
     Remember that c:foo <==> c:/foo.  */
 | 
						|
  if (isdrive (path))
 | 
						|
    {
 | 
						|
      *dir++ = *path++;
 | 
						|
      *dir++ = *path++;
 | 
						|
      *dir++ = '/';
 | 
						|
      if (!*path)
 | 
						|
	{
 | 
						|
	  *dir = 0;
 | 
						|
	  *file = 0;
 | 
						|
	  return;
 | 
						|
	}
 | 
						|
      if (isdirsep (*path))
 | 
						|
	++path;
 | 
						|
      dir_started_p = 1;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Determine if there are trailing slashes and "delete" them if present.
 | 
						|
     We pretend as if they don't exist.  */
 | 
						|
  const char *end = path + strlen (path);
 | 
						|
  /* path + 1: keep leading slash.  */
 | 
						|
  while (end > path + 1 && isdirsep (end[-1]))
 | 
						|
    --end;
 | 
						|
 | 
						|
  /* At this point, END points to one beyond the last character
 | 
						|
     (with trailing slashes "deleted").  */
 | 
						|
 | 
						|
  /* Point LAST_SLASH at the last slash (duh...).  */
 | 
						|
  const char *last_slash;
 | 
						|
  for (last_slash = end - 1; last_slash >= path; --last_slash)
 | 
						|
    if (isdirsep (*last_slash))
 | 
						|
      break;
 | 
						|
 | 
						|
  if (last_slash == path)
 | 
						|
    {
 | 
						|
      *dir++ = '/';
 | 
						|
      *dir = 0;
 | 
						|
    }
 | 
						|
  else if (last_slash > path)
 | 
						|
    {
 | 
						|
      memcpy (dir, path, last_slash - path);
 | 
						|
      dir[last_slash - path] = 0;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if (dir_started_p)
 | 
						|
	; /* nothing to do */
 | 
						|
      else
 | 
						|
	*dir++ = '.';
 | 
						|
      *dir = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  memcpy (file, last_slash + 1, end - last_slash - 1);
 | 
						|
  file[end - last_slash - 1] = 0;
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************************************************/
 | 
						|
 | 
						|
/* Return the hash value for the current win32 value.
 | 
						|
   This is used when constructing inodes. */
 | 
						|
DWORD
 | 
						|
cwdstuff::get_hash ()
 | 
						|
{
 | 
						|
  DWORD hashnow;
 | 
						|
  cwd_lock.acquire ();
 | 
						|
  hashnow = hash;
 | 
						|
  cwd_lock.release ();
 | 
						|
  return hashnow;
 | 
						|
}
 | 
						|
 | 
						|
/* Initialize cygcwd 'muto' for serializing access to cwd info. */
 | 
						|
void
 | 
						|
cwdstuff::init ()
 | 
						|
{
 | 
						|
  cwd_lock.init ("cwd_lock");
 | 
						|
}
 | 
						|
 | 
						|
/* Get initial cwd.  Should only be called once in a
 | 
						|
   process tree. */
 | 
						|
bool
 | 
						|
cwdstuff::get_initial ()
 | 
						|
{
 | 
						|
  cwd_lock.acquire ();
 | 
						|
 | 
						|
  if (win32)
 | 
						|
    return 1;
 | 
						|
 | 
						|
  /* Leaves cwd lock unreleased, if success */
 | 
						|
  return !set (NULL, NULL, false);
 | 
						|
}
 | 
						|
 | 
						|
/* Chdir and fill out the elements of a cwdstuff struct.
 | 
						|
   It is assumed that the lock for the cwd is acquired if
 | 
						|
   win32_cwd == NULL. */
 | 
						|
int
 | 
						|
cwdstuff::set (const char *win32_cwd, const char *posix_cwd, bool doit)
 | 
						|
{
 | 
						|
  char pathbuf[2 * CYG_MAX_PATH];
 | 
						|
  int res = -1;
 | 
						|
 | 
						|
  if (win32_cwd)
 | 
						|
    {
 | 
						|
       cwd_lock.acquire ();
 | 
						|
       if (doit && !SetCurrentDirectory (win32_cwd))
 | 
						|
	 {
 | 
						|
	    __seterrno ();
 | 
						|
	    goto out;
 | 
						|
	 }
 | 
						|
    }
 | 
						|
  /* If there is no win32 path or it has the form c:xxx, get the value */
 | 
						|
  if (!win32_cwd || (isdrive (win32_cwd) && win32_cwd[2] != '\\'))
 | 
						|
    {
 | 
						|
      int i;
 | 
						|
      DWORD len, dlen;
 | 
						|
      for (i = 0, dlen = CYG_MAX_PATH/3; i < 2; i++, dlen = len)
 | 
						|
	{
 | 
						|
	  win32 = (char *) crealloc (win32, dlen);
 | 
						|
	  if ((len = GetCurrentDirectoryA (dlen, win32)) < dlen)
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
      if (len == 0)
 | 
						|
	{
 | 
						|
	  __seterrno ();
 | 
						|
	  debug_printf ("GetCurrentDirectory, %E");
 | 
						|
	  win32_cwd = pathbuf; /* Force lock release */
 | 
						|
	  goto out;
 | 
						|
	}
 | 
						|
      posix_cwd = NULL;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      win32 = (char *) crealloc (win32, strlen (win32_cwd) + 1);
 | 
						|
      strcpy (win32, win32_cwd);
 | 
						|
    }
 | 
						|
  if (win32[1] == ':')
 | 
						|
    drive_length = 2;
 | 
						|
  else if (win32[1] == '\\')
 | 
						|
    {
 | 
						|
      char * ptr = strechr (win32 + 2, '\\');
 | 
						|
      if (*ptr)
 | 
						|
	ptr = strechr (ptr + 1, '\\');
 | 
						|
      drive_length = ptr - win32;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    drive_length = 0;
 | 
						|
 | 
						|
  if (!posix_cwd)
 | 
						|
    {
 | 
						|
      mount_table->conv_to_posix_path (win32, pathbuf, 0);
 | 
						|
      posix_cwd = pathbuf;
 | 
						|
    }
 | 
						|
  posix = (char *) crealloc (posix, strlen (posix_cwd) + 1);
 | 
						|
  strcpy (posix, posix_cwd);
 | 
						|
 | 
						|
  hash = hash_path_name (0, win32);
 | 
						|
 | 
						|
  res = 0;
 | 
						|
out:
 | 
						|
  if (win32_cwd)
 | 
						|
    cwd_lock.release ();
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
/* Copy the value for either the posix or the win32 cwd into a buffer. */
 | 
						|
char *
 | 
						|
cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
 | 
						|
{
 | 
						|
  MALLOC_CHECK;
 | 
						|
 | 
						|
  if (ulen)
 | 
						|
    /* nothing */;
 | 
						|
  else if (buf == NULL)
 | 
						|
    ulen = (unsigned) -1;
 | 
						|
  else
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      goto out;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!get_initial ())	/* Get initial cwd and set cwd lock */
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  char *tocopy;
 | 
						|
  if (!need_posix)
 | 
						|
    tocopy = win32;
 | 
						|
  else
 | 
						|
    tocopy = posix;
 | 
						|
 | 
						|
  debug_printf ("posix %s", posix);
 | 
						|
  if (strlen (tocopy) >= ulen)
 | 
						|
    {
 | 
						|
      set_errno (ERANGE);
 | 
						|
      buf = NULL;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if (!buf)
 | 
						|
	buf = (char *) malloc (strlen (tocopy) + 1);
 | 
						|
      strcpy (buf, tocopy);
 | 
						|
      if (!buf[0])	/* Should only happen when chroot */
 | 
						|
	strcpy (buf, "/");
 | 
						|
    }
 | 
						|
 | 
						|
  cwd_lock.release ();
 | 
						|
 | 
						|
out:
 | 
						|
  syscall_printf ("(%s) = cwdstuff::get (%p, %d, %d, %d), errno %d",
 | 
						|
		  buf, buf, ulen, need_posix, with_chroot, errno);
 | 
						|
  MALLOC_CHECK;
 | 
						|
  return buf;
 | 
						|
}
 | 
						|
 | 
						|
int etc::curr_ix = 0;
 | 
						|
/* Note that the first elements of the below arrays are unused */
 | 
						|
bool etc::change_possible[MAX_ETC_FILES + 1];
 | 
						|
const char *etc::fn[MAX_ETC_FILES + 1];
 | 
						|
FILETIME etc::last_modified[MAX_ETC_FILES + 1];
 | 
						|
 | 
						|
int
 | 
						|
etc::init (int n, const char *etc_fn)
 | 
						|
{
 | 
						|
  if (n > 0)
 | 
						|
    /* ok */;
 | 
						|
  else if (++curr_ix <= MAX_ETC_FILES)
 | 
						|
    n = curr_ix;
 | 
						|
  else
 | 
						|
    api_fatal ("internal error");
 | 
						|
 | 
						|
  fn[n] = etc_fn;
 | 
						|
  change_possible[n] = false;
 | 
						|
  test_file_change (n);
 | 
						|
  paranoid_printf ("fn[%d] %s, curr_ix %d", n, fn[n], curr_ix);
 | 
						|
  return n;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
etc::test_file_change (int n)
 | 
						|
{
 | 
						|
  HANDLE h;
 | 
						|
  WIN32_FIND_DATA data;
 | 
						|
  bool res;
 | 
						|
 | 
						|
  if ((h = FindFirstFile (fn[n], &data)) == INVALID_HANDLE_VALUE)
 | 
						|
    {
 | 
						|
      res = true;
 | 
						|
      memset (last_modified + n, 0, sizeof (last_modified[n]));
 | 
						|
      debug_printf ("FindFirstFile failed, %E");
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      FindClose (h);
 | 
						|
      res = CompareFileTime (&data.ftLastWriteTime, last_modified + n) > 0;
 | 
						|
      last_modified[n] = data.ftLastWriteTime;
 | 
						|
      debug_printf ("FindFirstFile succeeded");
 | 
						|
    }
 | 
						|
 | 
						|
  paranoid_printf ("fn[%d] %s res %d", n, fn[n], res);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
etc::dir_changed (int n)
 | 
						|
{
 | 
						|
  if (!change_possible[n])
 | 
						|
    {
 | 
						|
      static HANDLE changed_h NO_COPY;
 | 
						|
 | 
						|
      if (!changed_h)
 | 
						|
	{
 | 
						|
	  path_conv pwd ("/etc");
 | 
						|
	  changed_h = FindFirstChangeNotification (pwd, FALSE,
 | 
						|
						  FILE_NOTIFY_CHANGE_LAST_WRITE
 | 
						|
						  | FILE_NOTIFY_CHANGE_FILE_NAME);
 | 
						|
#ifdef DEBUGGING
 | 
						|
	  if (changed_h == INVALID_HANDLE_VALUE)
 | 
						|
	    system_printf ("Can't open %s for checking, %E", (char *) pwd);
 | 
						|
#endif
 | 
						|
	  memset (change_possible, true, sizeof (change_possible));
 | 
						|
	}
 | 
						|
 | 
						|
      if (changed_h == INVALID_HANDLE_VALUE)
 | 
						|
	change_possible[n] = true;
 | 
						|
      else if (WaitForSingleObject (changed_h, 0) == WAIT_OBJECT_0)
 | 
						|
	{
 | 
						|
	  FindNextChangeNotification (changed_h);
 | 
						|
	  memset (change_possible, true, sizeof change_possible);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  paranoid_printf ("fn[%d] %s change_possible %d", n, fn[n], change_possible[n]);
 | 
						|
  return change_possible[n];
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
etc::file_changed (int n)
 | 
						|
{
 | 
						|
  bool res = false;
 | 
						|
  if (dir_changed (n) && test_file_change (n))
 | 
						|
    res = true;
 | 
						|
  change_possible[n] = false;	/* Change is no longer possible */
 | 
						|
  paranoid_printf ("fn[%d] %s res %d", n, fn[n], res);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
/* No need to be reentrant or thread-safe according to SUSv3.
 | 
						|
   / and \\ are treated equally.  Leading drive specifiers are
 | 
						|
   kept intact as far as it makes sense.  Everything else is
 | 
						|
   POSIX compatible. */
 | 
						|
extern "C" char *
 | 
						|
basename (char *path)
 | 
						|
{
 | 
						|
  static char buf[CYG_MAX_PATH];
 | 
						|
  char *c, *d, *bs = buf;
 | 
						|
 | 
						|
  if (!path || !*path)
 | 
						|
    return strcpy (buf, ".");
 | 
						|
  strncpy (buf, path, CYG_MAX_PATH);
 | 
						|
  if (isalpha (buf[0]) && buf[1] == ':')
 | 
						|
    bs += 2;
 | 
						|
  else if (strspn (buf, "/\\") > 1)
 | 
						|
    ++bs;
 | 
						|
  c = strrchr (bs, '/');
 | 
						|
  if ((d = strrchr (c ?: bs, '\\')) > c)
 | 
						|
    c = d;
 | 
						|
  if (c)
 | 
						|
    {
 | 
						|
      /* Trailing (back)slashes are eliminated. */
 | 
						|
      while (c && c > bs && c[1] == '\0')
 | 
						|
	{
 | 
						|
	  *c = '\0';
 | 
						|
	  c = strrchr (bs, '/');
 | 
						|
	  if ((d = strrchr (c ?: bs, '\\')) > c)
 | 
						|
	    c = d;
 | 
						|
	}
 | 
						|
      if (c && (c > bs || c[1]))
 | 
						|
	return c + 1;
 | 
						|
    }
 | 
						|
  else if (!bs[0])
 | 
						|
    strcpy (bs, ".");
 | 
						|
  return buf;
 | 
						|
}
 | 
						|
 | 
						|
/* No need to be reentrant or thread-safe according to SUSv3.
 | 
						|
   / and \\ are treated equally.  Leading drive specifiers and
 | 
						|
   leading double (back)slashes are kept intact as far as it
 | 
						|
   makes sense.  Everything else is POSIX compatible. */
 | 
						|
extern "C" char *
 | 
						|
dirname (char *path)
 | 
						|
{
 | 
						|
  static char buf[CYG_MAX_PATH];
 | 
						|
  char *c, *d, *bs = buf;
 | 
						|
 | 
						|
  if (!path || !*path)
 | 
						|
    return strcpy (buf, ".");
 | 
						|
  strncpy (buf, path, CYG_MAX_PATH);
 | 
						|
  if (isalpha (buf[0]) && buf[1] == ':')
 | 
						|
    bs += 2;
 | 
						|
  else if (strspn (buf, "/\\") > 1)
 | 
						|
    ++bs;
 | 
						|
  c = strrchr (bs, '/');
 | 
						|
  if ((d = strrchr (c ?: bs, '\\')) > c)
 | 
						|
    c = d;
 | 
						|
  if (c)
 | 
						|
    {
 | 
						|
      /* Trailing (back)slashes are eliminated. */
 | 
						|
      while (c && c > bs && c[1] == '\0')
 | 
						|
	{
 | 
						|
	  *c = '\0';
 | 
						|
	  c = strrchr (bs, '/');
 | 
						|
	  if ((d = strrchr (c ?: bs, '\\')) > c)
 | 
						|
	    c = d;
 | 
						|
	}
 | 
						|
      if (!c)
 | 
						|
	strcpy (bs, ".");
 | 
						|
      else if (c > bs)
 | 
						|
	{
 | 
						|
	  /* More trailing (back)slashes are eliminated. */
 | 
						|
	  while (c > bs && (*c == '/' || *c == '\\'))
 | 
						|
	    *c-- = '\0';
 | 
						|
	}
 | 
						|
      else
 | 
						|
	c[1] = '\0';
 | 
						|
    }
 | 
						|
  else
 | 
						|
    strcpy (bs, ".");
 | 
						|
  return buf;
 | 
						|
}
 |