OSDN Git Service

* fhandler_clipboard.cc: new file
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / path.cc
index 54b8795..63a8efd 100644 (file)
@@ -62,7 +62,6 @@ details. */
 #include "cygerrno.h"
 #include "fhandler.h"
 #include "path.h"
-#include "thread.h"
 #include "sync.h"
 #include "sigproc.h"
 #include "pinfo.h"
@@ -70,6 +69,7 @@ details. */
 #include "shared_info.h"
 #include "registry.h"
 #include "security.h"
+#include <assert.h>
 
 static int normalize_win32_path (const char *src, char *dst);
 static void slashify (const char *src, char *dst, int trailing_slash_p);
@@ -168,6 +168,7 @@ path_conv::check (const char *src, unsigned opt,
   char path_copy[MAX_PATH];
   char tmp_buf[MAX_PATH];
   symlink_info sym;
+  bool need_directory = 0;
 
   char *rel_path, *full_path;
 
@@ -189,6 +190,16 @@ path_conv::check (const char *src, unsigned opt,
   for (;;)
     {
       MALLOC_CHECK;
+      assert (src);
+      char *p = strrchr (src, '/');
+      if (p)
+       {
+         if (p[1] == '\0' || strcmp (p, "/.") == 0)
+           need_directory = 1;
+       }
+      else if ((p = strrchr (src, '\\')) &&
+              (p[1] == '\0' || strcmp (p, "\\.") == 0))
+       need_directory = 1;
       /* Must look up path in mount table, etc.  */
       error = cygwin_shared->mount.conv_to_win32_path (src, rel_path,
                                                       full_path,
@@ -275,7 +286,7 @@ path_conv::check (const char *src, unsigned opt,
             these operations again on the newly derived path. */
          else if (len > 0)
            {
-             if (component == 0 && !(opt & PC_SYM_FOLLOW))
+             if (component == 0 && !need_directory && !(opt & PC_SYM_FOLLOW))
                {
                  set_symlink (); // last component of path is a symlink.
                  fileattr = sym.fileattr;
@@ -317,7 +328,6 @@ path_conv::check (const char *src, unsigned opt,
          }
 
       /* Copy tail of full_path to discovered symlink. */
-      char *p;
       for (p = sym.contents + buflen; *tail; tail++)
        *p++ = *tail == '\\' ? '/' : *tail;
       *p = '\0';
@@ -354,6 +364,18 @@ fillin:
     }
 
 out:
+  /* Deal with Windows stupidity which considers filename\. to be valid
+     even when "filename" is not a directory. */
+  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;
+    }
   DWORD serial, volflags;
 
   strcpy (tmp_buf, full_path);
@@ -408,6 +430,8 @@ const char *windows_device_names[] =
   "nul",
   "\\dev\\zero",
   "\\dev\\%srandom",
+  "\\dev\\mem",
+  "\\dev\\clipboard",
 };
 
 static int
@@ -477,6 +501,18 @@ get_device_number (const char *name, int &unit, BOOL from_conv)
          devn = FH_RANDOM;
          unit = 8 + (deveqn ("u", 1) ? 1 : 0); /* Keep unit Linux conformant */
        }
+      else if (deveq ("mem"))
+        {
+          devn = FH_MEM;
+          unit = 1;
+        }
+      else if (deveq ("clipboard"))
+        devn = FH_CLIPBOARD;
+      else if (deveq ("port"))
+        {
+          devn = FH_MEM;
+          unit = 4;
+        }
       else if (deveqn ("com", 3) && (unit = digits (name + 3)) >= 0)
        devn = FH_SERIAL;
       else if (deveq ("pipe") || deveq ("piper") || deveq ("pipew"))
@@ -540,22 +576,16 @@ normalize_posix_path (const char *src, char *dst)
     }
   if (!isslash (src[0]))
     {
-      char cwd[MAX_PATH];
-      if (!cygcwd.get (cwd))
+      if (!cygcwd.get (dst))
        return get_errno ();
-      if (strlen (cwd) + 1 + strlen (src) >= MAX_PATH)
+      if (strlen (dst) + 1 + strlen (src) >= MAX_PATH)
        {
          debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src);
          return ENAMETOOLONG;
        }
-      strcpy (dst, cwd);
       dst = strchr (dst, '\0');
       if (*src == '.')
-       {
-         if (dst == dst_start + 1)
-           dst--;
-         goto sawdot;
-       }
+       goto sawdot;
       if (dst > dst_start && !isslash (dst[-1]))
        *dst++ = '/';
     }
@@ -603,7 +633,11 @@ normalize_posix_path (const char *src, char *dst)
              if (src[1] != '.')
                {
                  if (!src[1])
-                   goto done;
+                   {
+                     if (dst == dst_start)
+                       *dst++ = '/';
+                     goto done;
+                   }
                  if (!isslash (src[1]))
                    break;
                }
@@ -647,17 +681,15 @@ normalize_win32_path (const char *src, char *dst)
 
   if (!SLASH_P (src[0]) && strchr (src, ':') == NULL)
     {
-      char cwd[MAX_PATH];
-      if (!cygcwd.get (cwd, 0))
+      if (!cygcwd.get (dst, 0))
        return get_errno ();
-      if (strlen (cwd) + 1 + strlen (src) >= MAX_PATH)
+      if (strlen (dst) + 1 + strlen (src) >= MAX_PATH)
        {
          debug_printf ("ENAMETOOLONG = normalize_win32_path (%s)", src);
          return ENAMETOOLONG;
        }
-      strcpy (dst, cwd);
       dst += strlen (dst);
-      if (!*cwd || !SLASH_P (dst[-1]))
+      if (!SLASH_P (dst[-1]))
        *dst++ = '\\';
     }
   /* Two leading \'s?  If so, preserve them.  */
@@ -1124,8 +1156,13 @@ mount_info::cygdrive_posix_path (const char *src, char *dst, int trailing_slash_
     dst[len++] = '\000';
   else
     {
+      int n;
       dst[len++] = '/';
-      strcpy (dst + len, src + 3);
+      if (SLASH_P (src[2]))
+       n = 3;
+      else
+       n = 2;
+      strcpy (dst + len, src + n);
     }
   slashify (dst, dst, trailing_slash_p);
 }
@@ -1269,13 +1306,13 @@ mount_info::read_mounts (reg_key& r)
   char posix_path[MAX_PATH];
   HKEY key = r.get_key ();
   DWORD i, posix_path_size;
-  int found_cygdrive = FALSE;
+  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 (DWORD i = 0; ; i++)
+  for (i = 0; ; i++)
     {
       char native_path[MAX_PATH];
       int mount_flags;
@@ -1284,23 +1321,17 @@ mount_info::read_mounts (reg_key& r)
       /* 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 MAX_PATH! */
-      LONG err = RegEnumKeyEx (key, i, posix_path, &posix_path_size, NULL,
+      res = RegEnumKeyEx (key, i, posix_path, &posix_path_size, NULL,
                          NULL, NULL, NULL);
 
-      if (err == ERROR_NO_MORE_ITEMS)
+      if (res == ERROR_NO_MORE_ITEMS)
        break;
-      else if (err != ERROR_SUCCESS)
+      else if (res != ERROR_SUCCESS)
        {
-         debug_printf ("RegEnumKeyEx failed, error %d!\n", err);
+         debug_printf ("RegEnumKeyEx failed, error %d!\n", res);
          break;
        }
 
-      if (iscygdrive (posix_path))
-       {
-         found_cygdrive = TRUE;
-         continue;
-       }
-
       /* Get a reg_key based on i. */
       reg_key subkey = reg_key (key, KEY_READ, posix_path, NULL);
 
@@ -1309,31 +1340,10 @@ mount_info::read_mounts (reg_key& r)
       mount_flags = subkey.get_int ("flags", 0);
 
       /* Add mount_item corresponding to registry mount point. */
-      int res = cygwin_shared->mount.add_item (native_path, posix_path, mount_flags, FALSE);
+      res = cygwin_shared->mount.add_item (native_path, posix_path, mount_flags, FALSE);
       if (res && get_errno () == EMFILE)
        break; /* The number of entries exceeds MAX_MOUNTS */
     }
-
-  if (!found_cygdrive)
-    return;
-
-loop:
-  for (i = 0; ;i++)
-    {
-      posix_path_size = MAX_PATH;
-      LONG err = RegEnumKeyEx (key, i, posix_path, &posix_path_size, NULL,
-                         NULL, NULL, NULL);
-
-      if (err != ERROR_SUCCESS)
-       break;
-
-      if (iscygdrive (posix_path))
-       {
-         /* This shouldn't be in the mount table. */
-         (void) r.kill (posix_path);
-         goto loop;
-       }
-    }
 }
 
 /* from_registry: Build the entire mount table from the registry.  Also,
@@ -1376,6 +1386,8 @@ mount_info::from_registry ()
 int
 mount_info::add_reg_mount (const char * native_path, const char * posix_path, unsigned mountflags)
 {
+  int res = 0;
+
   /* Add the mount to the right registry location, depending on
      whether MOUNT_SYSTEM is set in the mount flags. */
   if (!(mountflags & MOUNT_SYSTEM)) /* current_user mount */
@@ -1384,14 +1396,18 @@ mount_info::add_reg_mount (const char * native_path, const char * posix_path, un
       reg_key reg_user;
 
       /* Start by deleting existing mount if one exists. */
-      reg_user.kill (posix_path);
+      res = reg_user.kill (posix_path);
+      if (res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND)
+       goto err;
 
       /* Create the new mount. */
       reg_key subkey = reg_key (reg_user.get_key (),
                                KEY_ALL_ACCESS,
                                posix_path, NULL);
-      subkey.set_string ("native", native_path);
-      subkey.set_int ("flags", mountflags);
+      res = subkey.set_string ("native", native_path);
+      if (res != ERROR_SUCCESS)
+       goto err;
+      res = subkey.set_int ("flags", mountflags);
     }
   else /* local_machine mount */
     {
@@ -1402,24 +1418,25 @@ mount_info::add_reg_mount (const char * native_path, const char * posix_path, un
                       CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
                       NULL);
 
-      if (reg_sys.get_key () == INVALID_HANDLE_VALUE)
-       {
-         set_errno (EACCES);
-         return -1;
-       }
-
       /* Start by deleting existing mount if one exists. */
-      reg_sys.kill (posix_path);
+      res = reg_sys.kill (posix_path);
+      if (res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND)
+       goto err;
 
       /* Create the new mount. */
       reg_key subkey = reg_key (reg_sys.get_key (),
                                KEY_ALL_ACCESS,
                                posix_path, NULL);
-      subkey.set_string ("native", native_path);
-      subkey.set_int ("flags", mountflags);
+      res = subkey.set_string ("native", native_path);
+      if (res != ERROR_SUCCESS)
+       goto err;
+      res = subkey.set_int ("flags", mountflags);
     }
 
   return 0; /* Success! */
+ err:
+  __seterrno_from_win_error (res);
+  return -1;
 }
 
 /* del_reg_mount: delete mount item from registry indicated in flags.
@@ -1429,13 +1446,13 @@ mount_info::add_reg_mount (const char * native_path, const char * posix_path, un
 int
 mount_info::del_reg_mount (const char * posix_path, unsigned flags)
 {
-  int killres;
+  int res;
 
   if ((flags & MOUNT_SYSTEM) == 0)     /* Delete from user registry */
     {
       reg_key reg_user (KEY_ALL_ACCESS,
                        CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, NULL);
-      killres = reg_user.kill (posix_path);
+      res = reg_user.kill (posix_path);
     }
   else                                 /* Delete from system registry */
     {
@@ -1444,19 +1461,12 @@ mount_info::del_reg_mount (const char * posix_path, unsigned flags)
                       CYGWIN_INFO_CYGWIN_REGISTRY_NAME,
                       CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
                       NULL);
-
-      if (reg_sys.get_key () == INVALID_HANDLE_VALUE)
-       {
-         set_errno (EACCES);
-         return -1;
-       }
-
-      killres = reg_sys.kill (posix_path);
+      res = reg_sys.kill (posix_path);
     }
 
-  if (killres != ERROR_SUCCESS)
+  if (res != ERROR_SUCCESS)
     {
-      __seterrno_from_win_error (killres);
+      __seterrno_from_win_error (res);
       return -1;
     }
 
@@ -1541,7 +1551,13 @@ mount_info::write_cygdrive_info_to_registry (const char *cygdrive_prefix, unsign
   /* Ensure that there is never a final slash */
   nofinalslash (cygdrive_prefix, hold_cygdrive_prefix);
 
-  r.set_string ("cygdrive prefix", hold_cygdrive_prefix);
+  int res;
+  res = r.set_string ("cygdrive prefix", hold_cygdrive_prefix);
+  if (res != ERROR_SUCCESS)
+    {
+      __seterrno_from_win_error (res);
+      return -1;
+    }
   r.set_int ("cygdrive flags", flags);
 
   /* This also needs to go in the in-memory copy of "cygdrive", but only if
@@ -1711,7 +1727,8 @@ mount_info::add_item (const char *native, const char *posix, unsigned mountflags
 
   if ((native == NULL) || (*native == 0) ||
       (posix == NULL) || (*posix == 0) ||
-      (!slash_unc_prefix_p (native) && !isabspath (native)))
+      !isabspath (native) || !isabspath (posix) ||
+      slash_unc_prefix_p (posix) || isdrive (posix))
     {
       set_errno (EINVAL);
       return -1;
@@ -1753,20 +1770,17 @@ mount_info::add_item (const char *native, const char *posix, unsigned mountflags
        break;
     }
 
-  if (i == nmounts)
+  if (i == nmounts && nmounts == MAX_MOUNTS)
     {
-      if (nmounts < MAX_MOUNTS)
-       i = nmounts++;
-      else
-       {
-         set_errno (EMFILE);
-         return -1;
-       }
+      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 ();
 
@@ -2001,15 +2015,7 @@ mount (const char *win32_path, const char *posix_path, unsigned flags)
       win32_path = NULL;
     }
   else
-    {
-      if (iscygdrive (posix_path))
-       {
-         set_errno (EINVAL);
-         return res;   /* Don't try to add cygdrive prefix. */
-       }
-
-      res = cygwin_shared->mount.add_item (win32_path, posix_path, flags, TRUE);
-    }
+    res = cygwin_shared->mount.add_item (win32_path, posix_path, flags, TRUE);
 
   syscall_printf ("%d = mount (%s, %s, %p)", res, win32_path, posix_path, flags);
   return res;
@@ -2119,7 +2125,7 @@ symlink (const char *topath, const char *frompath)
       goto done;
     }
 
-  h = CreateFileA(win32_path.get_win32 (), GENERIC_WRITE, 0, &sec_none_nih,
+  h = CreateFileA(win32_path, GENERIC_WRITE, 0, &sec_none_nih,
                  CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
   if (h == INVALID_HANDLE_VALUE)
       __seterrno ();
@@ -2157,6 +2163,7 @@ done:
 static __inline char *
 has_suffix (const char *path, const suffix_info *suffixes)
 {
+  assert (path);
   char *ext = strrchr (path, '.');
   if (ext)
     for (const suffix_info *ex = suffixes; ex->name != NULL; ex++)
@@ -2240,21 +2247,6 @@ symlink_info::check (const char *in_path, const suffix_info *suffixes)
          continue;
        }
 
-      /* Windows allows path\. even when `path' isn't a directory.
-        Detect this scenario and disallow it, since it is non-UNIX like.
-        FIXME: This code actually checks for things like foo/ and foo/..
-        even though those usages have already been (erroneously?) eaten
-        by cygwin_shared->mount.conv_to_win32_path in path_conv::check. */
-
-      char *p = strrchr (path, '\\');
-      if (p && !(fileattr & FILE_ATTRIBUTE_DIRECTORY) &&
-         (*++p == '\0' || (*p == '.' && (*++p == '\0' || (*p == '.' && p[1] == '\0')))))
-       {
-         debug_printf ("%s is a non-directory", path);
-         error = ENOTDIR;
-         goto file_not_symlink;
-       }
-
       /* A symlink will have the `system' file attribute. */
       /* Only files can be symlinks (which can be symlinks to directories). */
       if (!(pflags & PATH_SYMLINK) && !SYMLINKATTR (fileattr))
@@ -2308,9 +2300,10 @@ symlink_info::check (const char *in_path, const suffix_info *suffixes)
          else
            {
              /* Not a symlink, see if executable.  */
-             if (!(pflags & (PATH_EXEC | PATH_CYGWIN_EXEC)) && got >= 2 &&
+             if (!(pflags & PATH_ALL_EXEC) && got >= 2 &&
                  ((cookie_buf[0] == '#' && cookie_buf[1] == '!') ||
-                  (cookie_buf[0] == ':' && cookie_buf[1] == '\n')))
+                  (cookie_buf[0] == ':' && cookie_buf[1] == '\n') ||
+                  (cookie_buf[0] == 'M' && cookie_buf[1] == 'Z')))
                pflags |= PATH_EXEC;
            close_and_return:
              CloseHandle (h);
@@ -2401,8 +2394,8 @@ hash_path_name (unsigned long hash, const char *name)
        {
          char *nn, *newname = (char *) alloca (strlen (name) + 2);
          nn = strncpy (newname, name, 2);
-         if (islower (*nn))
-           *newname = toupper (*nn);
+         if (isupper (*nn))
+           *newname = tolower (*nn);
          *(nn += 2) = '\0';
          name += 2;
          if (*name != '\\')
@@ -2420,12 +2413,12 @@ hash_path_name (unsigned long hash, const char *name)
         Otherwise the inodes same will differ depending on whether a file is
         referenced with an absolute value or relatively. */
 
-      if (*name != '\\')
+      if (!hash && !isabspath (name))
        {
          hash = cygcwd.get_hash ();
          if (name[0] == '.' && name[1] == '\0')
            return hash;
-         hash = hash_path_name (hash, "\\");
+         hash += hash_path_name (hash, "\\");
        }
     }
 
@@ -2434,7 +2427,8 @@ hashit:
      \a\b\.  but allow a single \ if that's all there is. */
   do
     {
-      hash += *name + (*name << 17);
+      int ch = tolower(*name);
+      hash += ch + (ch << 17);
       hash ^= hash >> 2;
     }
   while (*++name != '\0' &&
@@ -2445,36 +2439,7 @@ hashit:
 char *
 getcwd (char *buf, size_t ulen)
 {
-  char *res;
-  char *usebuf, uselen;
-
-  if (buf != NULL)
-    {
-      usebuf = buf;
-      uselen = TRUE;
-    }
-  else
-    {
-      if (ulen >= 0)
-       uselen = TRUE;
-      else
-       {
-         uselen = FALSE;
-         ulen = MAX_PATH + 1;
-       }
-
-      usebuf = (char *) malloc (ulen);
-      usebuf [ulen - 1] = '\0';
-    }
-
-  res = cygcwd.get (usebuf, 1, 1, ulen);
-
-  if (res && !uselen)
-    usebuf = (char *) realloc (usebuf, strlen (usebuf) + 1);
-  else if (!res && buf == NULL)
-    free (usebuf);
-
-  return res;
+  return cygcwd.get (buf, 1, 1, ulen);
 }
 
 /* getwd: standards? */
@@ -2490,6 +2455,7 @@ extern "C"
 int
 chdir (const char *dir)
 {
+  MALLOC_CHECK;
   syscall_printf ("dir %s", dir);
   path_conv path (dir, PC_FULL | PC_SYM_FOLLOW);
 
@@ -2538,6 +2504,7 @@ chdir (const char *dir)
      it was worth locking just for strace. */
   syscall_printf ("%d = chdir() cygcwd.posix '%s' native '%s'", res,
                  cygcwd.posix, native_dir);
+  MALLOC_CHECK;
   return res;
 }
 
@@ -2806,7 +2773,7 @@ cygwin_split_path (const char *path, char *dir, char *file)
     })
 
 /* Return TRUE if two strings match up to length n */
-int __stdcall
+extern "C" int __stdcall
 strncasematch (const char *s1, const char *s2, size_t n)
 {
   if (s1 == s2)
@@ -2823,7 +2790,7 @@ strncasematch (const char *s1, const char *s2, size_t n)
 }
 
 /* Return TRUE if two strings match */
-int __stdcall
+extern "C" int __stdcall
 strcasematch (const char *s1, const char *s2)
 {
   if (s1 == s2)
@@ -2838,7 +2805,7 @@ strcasematch (const char *s1, const char *s2)
   return *s2 == '\0';
 }
 
-char * __stdcall
+extern "C" char * __stdcall
 strcasestr (const char *searchee, const char *lookfor)
 {
   if (*searchee == 0)
@@ -2912,10 +2879,11 @@ cwdstuff::fixup_after_exec (char *win32_cwd, char *posix_cwd, DWORD hash_cwd)
 bool
 cwdstuff::get_initial ()
 {
+  lock->acquire ();
+
   if (win32)
     return 1;
 
-  lock->acquire ();
   int i;
   DWORD len, dlen;
   for (i = 0, dlen = MAX_PATH, len = 0; i < 3; dlen *= 2, i++)
@@ -2930,6 +2898,7 @@ cwdstuff::get_initial ()
       __seterrno ();
       lock->release ();
       debug_printf ("get_initial_cwd failed, %E");
+      lock->release ();
       return 0;
     }
   set (NULL);
@@ -2971,7 +2940,13 @@ cwdstuff::set (const char *win32_cwd, const char *posix_cwd)
 char *
 cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
 {
-  size_t len = ulen;
+  MALLOC_CHECK;
+
+  if (ulen == 0)
+    {
+      set_errno (EINVAL);
+      goto out;
+    }
 
   if (!get_initial ()) /* Get initial cwd and set cwd lock */
     return NULL;
@@ -2991,14 +2966,19 @@ cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
     }
   else
     {
+      if (!buf)
+       buf = (char *) malloc (strlen (tocopy) + 1);
       strcpy (buf, tocopy);
       if (!buf[0])     /* Should only happen when chroot */
        strcpy (buf, "/");
     }
 
   lock->release ();
-  syscall_printf ("(%s) = cwdstuff::get (%p, %d, %d, %d)",
-                 buf, buf, len, need_posix, with_chroot);
+
+out:
+  syscall_printf ("(%s) = cwdstuff::get (%p, %d, %d, %d), errno %d",
+                 buf, buf, ulen, need_posix, with_chroot, errno);
+  MALLOC_CHECK;
   return buf;
 }
 
@@ -3011,5 +2991,6 @@ cwdstuff::copy (char * &posix_cwd, char * &win32_cwd, DWORD hash_cwd)
   posix_cwd = cstrdup (posix);
   win32_cwd = cstrdup (win32);
   hash_cwd = hash;
+  MALLOC_CHECK;
   lock->release ();
 }