diff options
Diffstat (limited to 'winsup/cygwin/path.cc')
-rw-r--r-- | winsup/cygwin/path.cc | 2020 |
1 files changed, 1038 insertions, 982 deletions
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 631b169a0..e5ce312ed 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -651,561 +651,574 @@ path_conv::check (const char *src, unsigned opt, } #endif - myfault efault; - if (efault.faulted ()) - { - error = EFAULT; - return; - } - int loop = 0; - path_flags = 0; - known_suffix = NULL; - fileattr = INVALID_FILE_ATTRIBUTES; - caseinsensitive = OBJ_CASE_INSENSITIVE; - if (wide_path) - cfree (wide_path); - wide_path = NULL; - if (path) - { - cfree (modifiable_path ()); - path = NULL; - } - close_conv_handle (); - memset (&dev, 0, sizeof (dev)); - fs.clear (); - if (normalized_path) - { - cfree ((void *) normalized_path); - normalized_path = NULL; - } - int component = 0; // Number of translated components - - if (!(opt & PC_NULLEMPTY)) - error = 0; - else if (!*src) - { - error = ENOENT; - return; - } - - bool is_msdos = false; - /* This loop handles symlink expansion. */ - for (;;) - { - MALLOC_CHECK; - assert (src); - - is_relpath = !isabspath (src); - error = normalize_posix_path (src, path_copy, tail); - if (error > 0) - return; - if (error < 0) + __try + { + int loop = 0; + path_flags = 0; + known_suffix = NULL; + fileattr = INVALID_FILE_ATTRIBUTES; + caseinsensitive = OBJ_CASE_INSENSITIVE; + if (wide_path) + cfree (wide_path); + wide_path = NULL; + if (path) { - if (component == 0) - is_msdos = true; - error = 0; + cfree (modifiable_path ()); + path = NULL; } + close_conv_handle (); + memset (&dev, 0, sizeof (dev)); + fs.clear (); + if (normalized_path) + { + cfree ((void *) normalized_path); + normalized_path = NULL; + } + int component = 0; // Number of translated components - /* 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])) + if (!(opt & PC_NULLEMPTY)) + error = 0; + else if (!*src) { - need_directory = 1; - *--tail = '\0'; + error = ENOENT; + return; } - 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 | PC_KEEP_HANDLE); - ; - pflags_or = 0) + + bool is_msdos = false; + /* This loop handles symlink expansion. */ + for (;;) { - const suffix_info *suff; - char *full_path; + MALLOC_CHECK; + assert (src); - /* 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) + is_relpath = !isabspath (src); + error = normalize_posix_path (src, path_copy, tail); + if (error > 0) + return; + if (error < 0) { - suff = NULL; - full_path = pathbuf; + if (component == 0) + is_msdos = true; + error = 0; } - else + + /* 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])) { - suff = suffixes; - full_path = THIS_path; + 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 | PC_KEEP_HANDLE); + ; + pflags_or = 0) + { + const suffix_info *suff; + char *full_path; - /* Convert to native path spec sans symbolic link info. */ - error = mount_table->conv_to_win32_path (path_copy, full_path, dev, - &sym.pflags); + /* 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; + full_path = pathbuf; + } + else + { + suff = suffixes; + full_path = THIS_path; + } - if (error) - return; + /* Convert to native path spec sans symbolic link info. */ + error = mount_table->conv_to_win32_path (path_copy, full_path, + dev, &sym.pflags); - sym.pflags |= pflags_or; + if (error) + return; - if (!dev.exists ()) - { - error = ENXIO; - return; - } + sym.pflags |= pflags_or; - if (iscygdrive_dev (dev)) - { - if (!component) - fileattr = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY; - else + if (!dev.exists ()) { - fileattr = getfileattr (THIS_path, - sym.pflags & MOUNT_NOPOSIX); - dev = FH_FS; + error = ENXIO; + return; } - goto out; - } - else if (isdev_dev (dev)) - { - /* Just make sure that the path handling goes on as with FH_FS. */ - } - else if (isvirtual_dev (dev)) - { - /* FIXME: Calling build_fhandler here is not the right way to handle this. */ - fhandler_virtual *fh = (fhandler_virtual *) build_fh_dev (dev, path_copy); - virtual_ftype_t file_type; - if (!fh) - file_type = virt_none; - else + + if (iscygdrive_dev (dev)) { - file_type = fh->exists (); - if (file_type == virt_symlink) + if (!component) + fileattr = FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_READONLY; + else { - fh->fill_filebuf (); - symlen = sym.set (fh->get_filebuf ()); + fileattr = getfileattr (THIS_path, + sym.pflags & MOUNT_NOPOSIX); + dev = FH_FS; } - delete fh; + goto out; } - switch (file_type) + else if (isdev_dev (dev)) { - case virt_directory: - case virt_rootdir: - if (component == 0) - fileattr = FILE_ATTRIBUTE_DIRECTORY; - break; - case virt_file: - if (component == 0) - fileattr = 0; - break; - case virt_symlink: - goto is_virtual_symlink; - case virt_pipe: - if (component == 0) - { - fileattr = 0; - dev.parse (FH_PIPE); - } - break; - case virt_socket: - if (component == 0) - { - fileattr = 0; - dev.parse (FH_TCP); - } - break; - case virt_fsdir: - case virt_fsfile: - /* Access to real file or directory via block device - entry in /proc/sys. Convert to real file and go with - the flow. */ - dev.parse (FH_FS); - goto is_fs_via_procsys; - case virt_blk: - /* Block special device. If the trailing slash has been - requested, the target is the root directory of the - filesystem on this block device. So we convert this to - a real file and attach the backslash. */ - if (component == 0 && need_directory) - { + /* Make sure that the path handling goes on as with FH_FS. */ + } + else if (isvirtual_dev (dev)) + { + /* FIXME: Calling build_fhandler here is not the right way to + handle this. */ + fhandler_virtual *fh = (fhandler_virtual *) + build_fh_dev (dev, path_copy); + virtual_ftype_t file_type; + if (!fh) + file_type = virt_none; + else + { + file_type = fh->exists (); + if (file_type == virt_symlink) + { + fh->fill_filebuf (); + symlen = sym.set (fh->get_filebuf ()); + } + delete fh; + } + switch (file_type) + { + case virt_directory: + case virt_rootdir: + if (component == 0) + fileattr = FILE_ATTRIBUTE_DIRECTORY; + break; + case virt_file: + if (component == 0) + fileattr = 0; + break; + case virt_symlink: + goto is_virtual_symlink; + case virt_pipe: + if (component == 0) + { + fileattr = 0; + dev.parse (FH_PIPE); + } + break; + case virt_socket: + if (component == 0) + { + fileattr = 0; + dev.parse (FH_TCP); + } + break; + case virt_fsdir: + case virt_fsfile: + /* Access to real file or directory via block device + entry in /proc/sys. Convert to real file and go with + the flow. */ dev.parse (FH_FS); - strcat (full_path, "\\"); - fileattr = FILE_ATTRIBUTE_DIRECTORY - | FILE_ATTRIBUTE_DEVICE; - goto out; - } - /*FALLTHRU*/ - case virt_chr: - if (component == 0) - fileattr = FILE_ATTRIBUTE_DEVICE; - break; - default: - if (component == 0) - fileattr = INVALID_FILE_ATTRIBUTES; - goto virtual_component_retry; + goto is_fs_via_procsys; + case virt_blk: + /* Block special device. If the trailing slash has been + requested, the target is the root directory of the + filesystem on this block device. So we convert this + to a real file and attach the backslash. */ + if (component == 0 && need_directory) + { + dev.parse (FH_FS); + strcat (full_path, "\\"); + fileattr = FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_DEVICE; + goto out; + } + /*FALLTHRU*/ + case virt_chr: + if (component == 0) + fileattr = FILE_ATTRIBUTE_DEVICE; + break; + default: + if (component == 0) + fileattr = INVALID_FILE_ATTRIBUTES; + goto virtual_component_retry; + } + if (component == 0 || dev != FH_NETDRIVE) + path_flags |= PATH_RO; + goto out; } - if (component == 0 || dev != FH_NETDRIVE) - path_flags |= PATH_RO; - goto out; - } - /* devn should not be a device. If it is, then stop parsing now. */ - else if (dev != FH_FS) - { - fileattr = 0; - path_flags = sym.pflags; - if (component) + /* devn should not be a device. If it is, then stop parsing. */ + else if (dev != FH_FS) { - error = ENOTDIR; - return; + fileattr = 0; + path_flags = sym.pflags; + if (component) + { + error = ENOTDIR; + return; + } + goto out; /* Found a device. Stop parsing. */ } - 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'; - } + /* 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'; + } - /* If the incoming path was given in DOS notation, always treat - it as caseinsensitive,noacl path. This must be set before - calling sym.check, otherwise the path is potentially treated - casesensitive. */ - if (is_msdos) - sym.pflags |= PATH_NOPOSIX | PATH_NOACL; + /* If the incoming path was given in DOS notation, always treat + it as caseinsensitive,noacl path. This must be set before + calling sym.check, otherwise the path is potentially treated + casesensitive. */ + if (is_msdos) + sym.pflags |= PATH_NOPOSIX | PATH_NOACL; -is_fs_via_procsys: + is_fs_via_procsys: - symlen = sym.check (full_path, suff, fs, conv_handle); + symlen = sym.check (full_path, suff, fs, conv_handle); -is_virtual_symlink: + is_virtual_symlink: - if (sym.isdevice) - { - if (component) + if (sym.isdevice) { - error = ENOTDIR; - return; + if (component) + { + error = ENOTDIR; + return; + } + dev.parse (sym.major, sym.minor); + dev.setfs (1); + dev.mode = sym.mode; + fileattr = sym.fileattr; + goto out; } - 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) + if (sym.pflags & PATH_SOCKET) { - error = ENOTDIR; - return; + if (component) + { + error = ENOTDIR; + return; + } + fileattr = sym.fileattr; + dev.parse (FH_UNIX); + dev.setfs (1); + goto out; } - fileattr = sym.fileattr; - dev.parse (FH_UNIX); - dev.setfs (1); - goto out; - } - if (!component) - { - /* Make sure that /dev always exists. */ - fileattr = isdev_dev (dev) ? FILE_ATTRIBUTE_DIRECTORY - : sym.fileattr; - path_flags = sym.pflags; - } - else if (isdev_dev (dev)) - { - /* If we're looking for a file below /dev, which doesn't exist, - make sure that the device type is converted to FH_FS, so that - subsequent code handles the file correctly. - Unless /dev itself doesn't exist on disk. In that case /dev - is handled as virtual filesystem, and virtual filesystems are - read-only. The PC_KEEP_HANDLE check allows to check for - a call from an informational system call. In that case we - just stick to ENOENT, and the device type doesn't matter - anyway. */ - if (sym.error == ENOENT && !(opt & PC_KEEP_HANDLE)) - sym.error = EROFS; - else - dev = FH_FS; - } + if (!component) + { + /* Make sure that /dev always exists. */ + fileattr = isdev_dev (dev) ? FILE_ATTRIBUTE_DIRECTORY + : sym.fileattr; + path_flags = sym.pflags; + } + else if (isdev_dev (dev)) + { + /* If we're looking for a non-existing file below /dev, + make sure that the device type is converted to FH_FS, so + that subsequent code handles the file correctly. Unless + /dev itself doesn't exist on disk. In that case /dev + is handled as virtual filesystem, and virtual filesystems + are read-only. The PC_KEEP_HANDLE check allows to check + for a call from an informational system call. In that + case we just stick to ENOENT, and the device type doesn't + matter anyway. */ + if (sym.error == ENOENT && !(opt & PC_KEEP_HANDLE)) + sym.error = EROFS; + else + dev = FH_FS; + } - /* 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 = true; - else if (!(sym.fileattr & FILE_ATTRIBUTE_DIRECTORY)) + /* 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 = ENOTDIR; - goto out; + error = sym.error; + if (component == 0) + add_ext = true; + else if (!(sym.fileattr & FILE_ATTRIBUTE_DIRECTORY)) + { + error = ENOTDIR; + goto out; + } + goto out; // file found } - goto out; // file found - } - /* 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) - || (is_rep_symlink () && (opt & PC_SYM_NOFOLLOW_REP)))) + /* 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) { - set_symlink (symlen); // last component of path is a symlink. - if (opt & PC_SYM_CONTENTS) + saw_symlinks = 1; + if (component == 0 && !need_directory + && (!(opt & PC_SYM_FOLLOW) + || (is_rep_symlink () + && (opt & PC_SYM_NOFOLLOW_REP)))) { - strcpy (THIS_path, sym.contents); + /* last component of path is a symlink. */ + set_symlink (symlen); + if (opt & PC_SYM_CONTENTS) + { + strcpy (THIS_path, sym.contents); + goto out; + } + add_ext = true; goto out; } - add_ext = true; + /* Following a symlink we can't trust the collected + filesystem information any longer. */ + fs.clear (); + /* Close handle, if we have any. Otherwise we're collecting + handles while following symlinks. */ + conv_handle.close (); + break; + } + else if (sym.error && sym.error != ENOENT) + { + error = sym.error; goto out; } - /* Following a symlink we can't trust the collected filesystem - information any longer. */ - fs.clear (); - /* Close handle, if we have any. Otherwise we're collecting - handles while following symlinks. */ - conv_handle.close (); - break; + /* 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++; } - else if (sym.error && sym.error != ENOENT) + + /* Arrive here if above loop detected a symlink. */ + if (++loop > SYMLOOP_MAX) { - error = sym.error; - goto out; + error = ELOOP; // Eep. + return; } - /* 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++; - } + MALLOC_CHECK; - /* Arrive here if above loop detected a symlink. */ - if (++loop > SYMLOOP_MAX) - { - 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 + (2 * NT_MAX_PATH)) + { + too_long: + error = ENAMETOOLONG; + set_path ("::ENAMETOOLONG::"); + return; + } - /* Place the link content, possibly with head and/or tail, in tmp_buf */ + /* Copy the symlink contents to the end of tmp_buf. + Convert slashes. */ + for (char *p = sym.contents; *p; p++) + *headptr++ = *p == '\\' ? '/' : *p; + *headptr = '\0'; - 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]; + /* 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 + (2 * NT_MAX_PATH)) + goto too_long; + memcpy (headptr, tail, taillen); + } + + /* Evaluate everything all over again. */ + src = tmp_buf; } - /* Make sure there is enough space */ - if (headptr + symlen >= tmp_buf + (2 * NT_MAX_PATH)) + if (!(opt & PC_SYM_CONTENTS)) + add_ext = true; + + out: + set_path (THIS_path); + if (add_ext) + add_ext_from_sym (sym); + if (dev == FH_NETDRIVE && component) { - too_long: - error = ENAMETOOLONG; - set_path ("::ENAMETOOLONG::"); + /* 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 (isproc_dev (dev) && fileattr == INVALID_FILE_ATTRIBUTES) + { + /* FIXME: Usually we don't set error to ENOENT if a file doesn't + exist. This is typically indicated by the fileattr content. + So, why here? The downside is that cygwin_conv_path just gets + an error for these paths so it reports the error back to the + application. Unlike in all other cases of non-existant files, + for which check doesn't set error, so cygwin_conv_path just + returns the path, as intended. */ + error = ENOENT; 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) + else if (!need_directory || error) + /* nothing to do */; + else if (fileattr == INVALID_FILE_ATTRIBUTES) + /* Reattach trailing dirsep in native path. */ + strcat (modifiable_path (), "\\"); + else if (fileattr & FILE_ATTRIBUTE_DIRECTORY) + path_flags &= ~PATH_SYMLINK; + else { - /* Add a slash if needed. There is space. */ - if (*(headptr - 1) != '/') - *headptr++ = '/'; - int taillen = path_end - tail + 1; - if (headptr + taillen > tmp_buf + (2 * NT_MAX_PATH)) - goto too_long; - memcpy (headptr, tail, taillen); + debug_printf ("%s is a non-directory", path); + error = ENOTDIR; + return; } - /* Evaluate everything all over again. */ - src = tmp_buf; - } - - if (!(opt & PC_SYM_CONTENTS)) - add_ext = true; - -out: - set_path (THIS_path); - if (add_ext) - add_ext_from_sym (sym); - if (dev == 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 (isproc_dev (dev) && fileattr == INVALID_FILE_ATTRIBUTES) - { - /* FIXME: Usually we don't set error to ENOENT if a file doesn't - exist. This is typically indicated by the fileattr content. - So, why here? The downside is that cygwin_conv_path just gets - an error for these paths so it reports the error back to the - application. Unlike in all other cases of non-existant files, - for which check doesn't set error, so cygwin_conv_path just - returns the path, as intended. */ - error = ENOENT; - return; - } - else if (!need_directory || error) - /* nothing to do */; - else if (fileattr == INVALID_FILE_ATTRIBUTES) - strcat (modifiable_path (), "\\"); /* Reattach trailing dirsep in native path. */ - 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)) + if (dev.isfs ()) { - if (!tail || tail == path) - /* nothing */; - else if (tail[-1] != '\\') - *tail = '\0'; - else + if (strncmp (path, "\\\\.\\", 4)) { - error = ENOENT; - return; + if (!tail || tail == path) + /* nothing */; + else if (tail[-1] != '\\') + *tail = '\0'; + else + { + error = ENOENT; + return; + } } - } - /* If FS hasn't been checked already in symlink_info::check, do so now. */ - if (fs.inited ()|| fs.update (get_nt_native_path (), NULL)) - { - /* Incoming DOS paths are treated like DOS paths in native - Windows applications. No ACLs, just default settings. */ - if (is_msdos) - fs.has_acls (false); - debug_printf ("this->path(%s), has_acls(%d)", path, fs.has_acls ()); - /* CV: We could use this->has_acls() but I want to make sure that - we don't forget that the PATH_NOACL flag must be taken into - account here. */ - if (!(path_flags & PATH_NOACL) && fs.has_acls ()) - 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 FS hasn't been checked already in symlink_info::check, + do so now. */ + if (fs.inited ()|| fs.update (get_nt_native_path (), NULL)) + { + /* Incoming DOS paths are treated like DOS paths in native + Windows applications. No ACLs, just default settings. */ + if (is_msdos) + fs.has_acls (false); + debug_printf ("this->path(%s), has_acls(%d)", + path, fs.has_acls ()); + /* CV: We could use this->has_acls() but I want to make sure that + we don't forget that the PATH_NOACL flag must be taken into + account here. */ + if (!(path_flags & PATH_NOACL) && fs.has_acls ()) + 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 the FS has been found to have unrelibale inodes, note + that in path_flags. */ + if (!fs.hasgood_inode ()) + path_flags |= PATH_IHASH; + /* If the OS is caseinsensitive or the FS is caseinsensitive, + don't handle path casesensitive. */ + if (cygwin_shared->obcaseinsensitive || fs.caseinsensitive ()) + path_flags |= PATH_NOPOSIX; + caseinsensitive = (path_flags & PATH_NOPOSIX) + ? OBJ_CASE_INSENSITIVE : 0; + if (exec_state () != dont_know_if_executable) + /* ok */; + else if (isdir ()) + set_exec (1); + else if (issymlink () || issocket ()) + set_exec (0); } - /* If the FS has been found to have unrelibale inodes, note - that in path_flags. */ - if (!fs.hasgood_inode ()) - path_flags |= PATH_IHASH; - /* If the OS is caseinsensitive or the FS is caseinsensitive, - don't handle path casesensitive. */ - if (cygwin_shared->obcaseinsensitive || fs.caseinsensitive ()) - path_flags |= PATH_NOPOSIX; - caseinsensitive = (path_flags & PATH_NOPOSIX) - ? OBJ_CASE_INSENSITIVE : 0; - if (exec_state () != dont_know_if_executable) - /* ok */; - else if (isdir ()) - set_exec (1); - else if (issymlink () || issocket ()) - set_exec (0); - } - if (opt & PC_NOFULL) - { - if (is_relpath) + if (opt & PC_NOFULL) { - mkrelpath (this->modifiable_path (), !!caseinsensitive); - /* Invalidate wide_path so that wide relpath can be created - in later calls to get_nt_native_path or get_wide_win32_path. */ - if (wide_path) - cfree (wide_path); - wide_path = NULL; - } - 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)) + if (is_relpath) + { + mkrelpath (this->modifiable_path (), !!caseinsensitive); + /* Invalidate wide_path so that wide relpath can be created + in later calls to get_nt_native_path or get_wide_win32_path. */ + if (wide_path) + cfree (wide_path); + wide_path = NULL; + } + if (need_directory) { - this->modifiable_path ()[n] = '\\'; - this->modifiable_path ()[n + 1] = '\0'; + 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->modifiable_path ()[n] = '\\'; + this->modifiable_path ()[n + 1] = '\0'; + } } } - } - if (saw_symlinks) - set_has_symlinks (); + if (saw_symlinks) + set_has_symlinks (); - if (opt & PC_OPEN) - path_flags |= PATH_OPEN; + if (opt & PC_OPEN) + path_flags |= PATH_OPEN; - if (opt & PC_CTTY) - path_flags |= PATH_CTTY; + if (opt & PC_CTTY) + path_flags |= PATH_CTTY; - if (opt & PC_POSIX) - { - if (tail < path_end && tail > path_copy + 1) - *tail = '/'; - set_normalized_path (path_copy); - if (is_msdos && !(opt & PC_NOWARN)) - warn_msdos (src); - } + if (opt & PC_POSIX) + { + if (tail < path_end && tail > path_copy + 1) + *tail = '/'; + set_normalized_path (path_copy); + if (is_msdos && !(opt & PC_NOWARN)) + warn_msdos (src); + } #if 0 - if (!error) + if (!error) + { + last_path_conv = *this; + strcpy (last_src, src); + } +#endif + } + __except (NO_ERROR) { - last_path_conv = *this; - strcpy (last_src, src); + error = EFAULT; } -#endif + __endtry } path_conv::~path_conv () @@ -1688,332 +1701,342 @@ symlink_worker (const char *oldpath, const char *newpath, bool isdevice) /* 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) + __try { - set_errno (ENOENT); - goto done; - } - - if (strlen (oldpath) > SYMLINK_MAX) - { - set_errno (ENAMETOOLONG); - goto done; - } + if (!*oldpath || !*newpath) + { + set_errno (ENOENT); + __leave; + } - /* Trailing dirsep is a no-no. */ - len = strlen (newpath); - has_trailing_dirsep = isdirsep (newpath[len - 1]); - if (has_trailing_dirsep) - { - newpath = strdup (newpath); - ((char *) newpath)[len - 1] = '\0'; - } + if (strlen (oldpath) > SYMLINK_MAX) + { + set_errno (ENAMETOOLONG); + __leave; + } - check_opt = PC_SYM_NOFOLLOW | PC_POSIX | (isdevice ? PC_NOWARN : 0); - /* We need the normalized full path below. */ - win32_newpath.check (newpath, check_opt, stat_suffixes); - - /* Default symlink type is determined by global allow_winsymlinks variable. - Device files are always shortcuts. */ - wsym_type = isdevice ? WSYM_lnk : allow_winsymlinks; - /* NFS has its own, dedicated way to create symlinks. */ - if (win32_newpath.fs_is_nfs ()) - wsym_type = WSYM_nfs; - /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O - attribute. Therefore we create symlinks on MVFS always as shortcuts. */ - else if (win32_newpath.fs_is_mvfs ()) - wsym_type = WSYM_lnk; - /* AFS only supports native symlinks. */ - else if (win32_newpath.fs_is_afs ()) - { - /* Bail out if OS doesn't support native symlinks. */ - if (wincap.max_sys_priv () < SE_CREATE_SYMBOLIC_LINK_PRIVILEGE) + /* Trailing dirsep is a no-no. */ + len = strlen (newpath); + has_trailing_dirsep = isdirsep (newpath[len - 1]); + if (has_trailing_dirsep) { - set_errno (EPERM); - goto done; + newpath = strdup (newpath); + ((char *) newpath)[len - 1] = '\0'; } - wsym_type = WSYM_nativestrict; - } - /* Don't try native symlinks on filesystems not supporting reparse points. */ - else if ((wsym_type == WSYM_native || wsym_type == WSYM_nativestrict) - && !(win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS)) - wsym_type = WSYM_sysfile; - - /* Attach .lnk suffix when shortcut is requested. */ - if (wsym_type == WSYM_lnk && !win32_newpath.exists () - && (isdevice || !win32_newpath.fs_is_nfs ())) - { - char *newplnk = tp.c_get (); - stpcpy (stpcpy (newplnk, newpath), ".lnk"); - win32_newpath.check (newplnk, check_opt); - } - if (win32_newpath.error) - { - set_errno (win32_newpath.error); - goto done; - } + check_opt = PC_SYM_NOFOLLOW | PC_POSIX | (isdevice ? PC_NOWARN : 0); + /* We need the normalized full path below. */ + win32_newpath.check (newpath, check_opt, stat_suffixes); + + /* Default symlink type is determined by global allow_winsymlinks + variable. Device files are always shortcuts. */ + wsym_type = isdevice ? WSYM_lnk : allow_winsymlinks; + /* NFS has its own, dedicated way to create symlinks. */ + if (win32_newpath.fs_is_nfs ()) + wsym_type = WSYM_nfs; + /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O + attribute. Therefore we create symlinks on MVFS always as shortcuts. */ + else if (win32_newpath.fs_is_mvfs ()) + wsym_type = WSYM_lnk; + /* AFS only supports native symlinks. */ + else if (win32_newpath.fs_is_afs ()) + { + /* Bail out if OS doesn't support native symlinks. */ + if (wincap.max_sys_priv () < SE_CREATE_SYMBOLIC_LINK_PRIVILEGE) + { + set_errno (EPERM); + __leave; + } + wsym_type = WSYM_nativestrict; + } + /* Don't try native symlinks on FSes not supporting reparse points. */ + else if ((wsym_type == WSYM_native || wsym_type == WSYM_nativestrict) + && !(win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS)) + wsym_type = WSYM_sysfile; + + /* Attach .lnk suffix when shortcut is requested. */ + if (wsym_type == WSYM_lnk && !win32_newpath.exists () + && (isdevice || !win32_newpath.fs_is_nfs ())) + { + char *newplnk = tp.c_get (); + stpcpy (stpcpy (newplnk, newpath), ".lnk"); + win32_newpath.check (newplnk, check_opt); + } - syscall_printf ("symlink (%s, %S) wsym_type %d", oldpath, - win32_newpath.get_nt_native_path (), wsym_type); + if (win32_newpath.error) + { + set_errno (win32_newpath.error); + __leave; + } - if ((!isdevice && win32_newpath.exists ()) - || win32_newpath.is_auto_device ()) - { - set_errno (EEXIST); - goto done; - } - if (has_trailing_dirsep && !win32_newpath.exists ()) - { - set_errno (ENOENT); - goto done; - } + syscall_printf ("symlink (%s, %S) wsym_type %d", oldpath, + win32_newpath.get_nt_native_path (), wsym_type); - /* Handle NFS and native symlinks in their own functions. */ - switch (wsym_type) - { - case WSYM_nfs: - res = symlink_nfs (oldpath, win32_newpath); - goto done; - case WSYM_native: - case WSYM_nativestrict: - res = symlink_native (oldpath, win32_newpath); - if (!res) - goto done; - /* Strictly native? Too bad. */ - if (wsym_type == WSYM_nativestrict) + if ((!isdevice && win32_newpath.exists ()) + || win32_newpath.is_auto_device ()) { - __seterrno (); - goto done; + set_errno (EEXIST); + __leave; + } + if (has_trailing_dirsep && !win32_newpath.exists ()) + { + set_errno (ENOENT); + __leave; } - /* Otherwise, fall back to default symlink type. */ - wsym_type = WSYM_sysfile; - break; - default: - break; - } - - if (wsym_type == WSYM_lnk) - { - path_conv win32_oldpath; - ITEMIDLIST *pidl = NULL; - size_t full_len = 0; - unsigned short oldpath_len, desc_len, relpath_len, pidl_len = 0; - char desc[MAX_PATH + 1], *relpath; - if (!isdevice) + /* Handle NFS and native symlinks in their own functions. */ + switch (wsym_type) { - /* First create an IDLIST to learn how big our shortcut is - going to be. */ - IShellFolder *psl; - - /* The symlink target is relative to the directory in which - the symlink gets created, not relative to the cwd. Therefore - we have to mangle the path quite a bit before calling path_conv. */ - if (isabspath (oldpath)) - win32_oldpath.check (oldpath, - PC_SYM_NOFOLLOW, - stat_suffixes); - else + case WSYM_nfs: + res = symlink_nfs (oldpath, win32_newpath); + __leave; + case WSYM_native: + case WSYM_nativestrict: + res = symlink_native (oldpath, win32_newpath); + if (!res) + __leave; + /* Strictly native? Too bad. */ + if (wsym_type == WSYM_nativestrict) { - len = strrchr (win32_newpath.normalized_path, '/') - - win32_newpath.normalized_path + 1; - char *absoldpath = tp.t_get (); - stpcpy (stpncpy (absoldpath, win32_newpath.normalized_path, len), - oldpath); - win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes); + __seterrno (); + __leave; } - if (SUCCEEDED (SHGetDesktopFolder (&psl))) + /* Otherwise, fall back to default symlink type. */ + wsym_type = WSYM_sysfile; + break; + default: + break; + } + + if (wsym_type == WSYM_lnk) + { + path_conv win32_oldpath; + ITEMIDLIST *pidl = NULL; + size_t full_len = 0; + unsigned short oldpath_len, desc_len, relpath_len, pidl_len = 0; + char desc[MAX_PATH + 1], *relpath; + + if (!isdevice) { - WCHAR wc_path[win32_oldpath.get_wide_win32_path_len () + 1]; - win32_oldpath.get_wide_win32_path (wc_path); - /* Amazing but true: Even though the ParseDisplayName method - takes a wide char path name, it does not understand the - Win32 prefix for long pathnames! So we have to tack off - the prefix and convert the path to the "normal" syntax - for ParseDisplayName. */ - WCHAR *wc = wc_path + 4; - if (wc[1] != L':') /* native UNC path */ - *(wc += 2) = L'\\'; - HRESULT res; - if (SUCCEEDED (res = psl->ParseDisplayName (NULL, NULL, wc, NULL, - &pidl, NULL))) + /* First create an IDLIST to learn how big our shortcut is + going to be. */ + IShellFolder *psl; + + /* The symlink target is relative to the directory in which the + symlink gets created, not relative to the cwd. Therefore we + have to mangle the path quite a bit before calling path_conv.*/ + if (isabspath (oldpath)) + win32_oldpath.check (oldpath, + PC_SYM_NOFOLLOW, + stat_suffixes); + else + { + len = strrchr (win32_newpath.normalized_path, '/') + - win32_newpath.normalized_path + 1; + char *absoldpath = tp.t_get (); + stpcpy (stpncpy (absoldpath, win32_newpath.normalized_path, + len), + oldpath); + win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, + stat_suffixes); + } + if (SUCCEEDED (SHGetDesktopFolder (&psl))) { - ITEMIDLIST *p; + WCHAR wc_path[win32_oldpath.get_wide_win32_path_len () + 1]; + win32_oldpath.get_wide_win32_path (wc_path); + /* Amazing but true: Even though the ParseDisplayName method + takes a wide char path name, it does not understand the + Win32 prefix for long pathnames! So we have to tack off + the prefix and convert the path to the "normal" syntax + for ParseDisplayName. */ + WCHAR *wc = wc_path + 4; + if (wc[1] != L':') /* native UNC path */ + *(wc += 2) = L'\\'; + HRESULT res; + if (SUCCEEDED (res = psl->ParseDisplayName (NULL, NULL, wc, + NULL, &pidl, + NULL))) + { + ITEMIDLIST *p; - for (p = pidl; p->mkid.cb > 0; - p = (ITEMIDLIST *)((char *) p + p->mkid.cb)) - ; - pidl_len = (char *) p - (char *) pidl + 2; + for (p = pidl; p->mkid.cb > 0; + p = (ITEMIDLIST *)((char *) p + p->mkid.cb)) + ; + pidl_len = (char *) p - (char *) pidl + 2; + } + psl->Release (); } - psl->Release (); } + /* Compute size of shortcut file. */ + full_len = sizeof (win_shortcut_hdr); + if (pidl_len) + full_len += sizeof (unsigned short) + pidl_len; + oldpath_len = strlen (oldpath); + /* Unfortunately the length of the description is restricted to a + length of 2000 bytes. We don't want to add considerations for + the different lengths and even 2000 bytes is not enough for long + path names. So what we do here is to set the description to the + POSIX path only if the path is not longer than MAX_PATH characters. + We append the full path name after the regular shortcut data + (see below), which works fine with Windows Explorer as well + as older Cygwin versions (as long as the whole file isn't bigger + than 8K). The description field is only used for backward + compatibility to older Cygwin versions and those versions are + not capable of handling long path names anyway. */ + desc_len = stpcpy (desc, oldpath_len > MAX_PATH + ? "[path too long]" : oldpath) - desc; + full_len += sizeof (unsigned short) + desc_len; + /* Devices get the oldpath string unchanged as relative path. */ + if (isdevice) + { + relpath_len = oldpath_len; + stpcpy (relpath = tp.c_get (), oldpath); + } + else + { + relpath_len = strlen (win32_oldpath.get_win32 ()); + stpcpy (relpath = tp.c_get (), win32_oldpath.get_win32 ()); + } + full_len += sizeof (unsigned short) + relpath_len; + full_len += sizeof (unsigned short) + oldpath_len; + /* 1 byte more for trailing 0 written by stpcpy. */ + if (full_len < NT_MAX_PATH * sizeof (WCHAR)) + buf = tp.t_get (); + else + buf = (char *) alloca (full_len + 1); + + /* Create shortcut header */ + win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf; + 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); + if (pidl) + shortcut_header->flags |= WSH_FLAG_IDLIST; + shortcut_header->run = SW_NORMAL; + cp = buf + sizeof (win_shortcut_hdr); + + /* Create IDLIST */ + if (pidl) + { + *(unsigned short *)cp = pidl_len; + memcpy (cp += 2, pidl, pidl_len); + cp += pidl_len; + CoTaskMemFree (pidl); + } + + /* Create description */ + *(unsigned short *)cp = desc_len; + cp = stpcpy (cp += 2, desc); + + /* Create relpath */ + *(unsigned short *)cp = relpath_len; + cp = stpcpy (cp += 2, relpath); + + /* Append the POSIX path after the regular shortcut data for + the long path support. */ + unsigned short *plen = (unsigned short *) cp; + cp += 2; + *(PWCHAR) cp = 0xfeff; /* BOM */ + cp += 2; + *plen = sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) + * sizeof (WCHAR); + cp += *plen; } - /* Compute size of shortcut file. */ - full_len = sizeof (win_shortcut_hdr); - if (pidl_len) - full_len += sizeof (unsigned short) + pidl_len; - oldpath_len = strlen (oldpath); - /* Unfortunately the length of the description is restricted to a - length of 2000 bytes. We don't want to add considerations for - the different lengths and even 2000 bytes is not enough for long - path names. So what we do here is to set the description to the - POSIX path only if the path is not longer than MAX_PATH characters. - We append the full path name after the regular shortcut data - (see below), which works fine with Windows Explorer as well - as older Cygwin versions (as long as the whole file isn't bigger - than 8K). The description field is only used for backward - compatibility to older Cygwin versions and those versions are - not capable of handling long path names anyway. */ - desc_len = stpcpy (desc, oldpath_len > MAX_PATH - ? "[path too long]" : oldpath) - desc; - full_len += sizeof (unsigned short) + desc_len; - /* Devices get the oldpath string unchanged as relative path. */ - if (isdevice) - { - relpath_len = oldpath_len; - stpcpy (relpath = tp.c_get (), oldpath); - } - else - { - relpath_len = strlen (win32_oldpath.get_win32 ()); - stpcpy (relpath = tp.c_get (), win32_oldpath.get_win32 ()); - } - full_len += sizeof (unsigned short) + relpath_len; - full_len += sizeof (unsigned short) + oldpath_len; - /* 1 byte more for trailing 0 written by stpcpy. */ - if (full_len < NT_MAX_PATH * sizeof (WCHAR)) - buf = tp.t_get (); else - buf = (char *) alloca (full_len + 1); - - /* Create shortcut header */ - win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf; - 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); - if (pidl) - shortcut_header->flags |= WSH_FLAG_IDLIST; - shortcut_header->run = SW_NORMAL; - cp = buf + sizeof (win_shortcut_hdr); - - /* Create IDLIST */ - if (pidl) { - *(unsigned short *)cp = pidl_len; - memcpy (cp += 2, pidl, pidl_len); - cp += pidl_len; - CoTaskMemFree (pidl); + /* Default technique creating a symlink. */ + buf = tp.t_get (); + cp = stpcpy (buf, SYMLINK_COOKIE); + *(PWCHAR) cp = 0xfeff; /* BOM */ + cp += 2; + /* Note that the terminating nul is written. */ + cp += sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) + * sizeof (WCHAR); } - /* Create description */ - *(unsigned short *)cp = desc_len; - cp = stpcpy (cp += 2, desc); - - /* Create relpath */ - *(unsigned short *)cp = relpath_len; - cp = stpcpy (cp += 2, relpath); - - /* Append the POSIX path after the regular shortcut data for - the long path support. */ - unsigned short *plen = (unsigned short *) cp; - cp += 2; - *(PWCHAR) cp = 0xfeff; /* BOM */ - cp += 2; - *plen = sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) * sizeof (WCHAR); - cp += *plen; - } - else - { - /* Default technique creating a symlink. */ - buf = tp.t_get (); - cp = stpcpy (buf, SYMLINK_COOKIE); - *(PWCHAR) cp = 0xfeff; /* BOM */ - cp += 2; - /* Note that the terminating nul is written. */ - cp += sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) * sizeof (WCHAR); - } - - OBJECT_ATTRIBUTES attr; - IO_STATUS_BLOCK io; - NTSTATUS status; - ULONG access; - HANDLE fh; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + ULONG access; + HANDLE fh; - access = DELETE | FILE_GENERIC_WRITE; - if (isdevice && win32_newpath.exists ()) - { - status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, - win32_newpath.get_object_attr (attr, sec_none_nih), - &io, 0, FILE_OPEN_FOR_BACKUP_INTENT); + access = DELETE | FILE_GENERIC_WRITE; + if (isdevice && win32_newpath.exists ()) + { + status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, + win32_newpath.get_object_attr (attr, + sec_none_nih), + &io, 0, FILE_OPEN_FOR_BACKUP_INTENT); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + __leave; + } + status = NtSetAttributesFile (fh, FILE_ATTRIBUTE_NORMAL); + NtClose (fh); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + __leave; + } + } + else if (!isdevice && win32_newpath.has_acls () + && !win32_newpath.isremote ()) + /* If the filesystem supports ACLs, we will overwrite the DACL after the + call to NtCreateFile. This requires a handle with READ_CONTROL and + WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to + open the file again. + FIXME: On remote NTFS shares open sometimes fails because even the + creator of the file doesn't have the right to change the DACL. + I don't know what setting that is or how to recognize such a share, + so for now we don't request WRITE_DAC on remote drives. */ + access |= READ_CONTROL | WRITE_DAC; + + status = NtCreateFile (&fh, access, + win32_newpath.get_object_attr (attr, sec_none_nih), + &io, NULL, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_VALID_FLAGS, + isdevice ? FILE_OVERWRITE_IF : FILE_CREATE, + FILE_SYNCHRONOUS_IO_NONALERT + | FILE_NON_DIRECTORY_FILE + | FILE_OPEN_FOR_BACKUP_INTENT, + NULL, 0); if (!NT_SUCCESS (status)) { __seterrno_from_nt_status (status); - goto done; + __leave; } - status = NtSetAttributesFile (fh, FILE_ATTRIBUTE_NORMAL); - NtClose (fh); - if (!NT_SUCCESS (status)) + if (win32_newpath.has_acls ()) + set_file_attribute (fh, win32_newpath, ILLEGAL_UID, ILLEGAL_GID, + (io.Information == FILE_CREATED ? S_JUSTCREATED : 0) + | S_IFLNK | STD_RBITS | STD_WBITS); + status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf, + NULL, NULL); + if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf)) + { + status = NtSetAttributesFile (fh, wsym_type == WSYM_lnk + ? FILE_ATTRIBUTE_READONLY + : FILE_ATTRIBUTE_SYSTEM); + if (!NT_SUCCESS (status)) + debug_printf ("Setting attributes failed, status = %y", status); + res = 0; + } + else { __seterrno_from_nt_status (status); - goto done; + FILE_DISPOSITION_INFORMATION fdi = { TRUE }; + status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi, + FileDispositionInformation); + if (!NT_SUCCESS (status)) + debug_printf ("Setting delete dispostion failed, status = %y", + status); } - } - else if (!isdevice && win32_newpath.has_acls () && !win32_newpath.isremote ()) - /* If the filesystem supports ACLs, we will overwrite the DACL after the - call to NtCreateFile. This requires a handle with READ_CONTROL and - WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to - open the file again. - FIXME: On remote NTFS shares open sometimes fails because even the - creator of the file doesn't have the right to change the DACL. - I don't know what setting that is or how to recognize such a share, - so for now we don't request WRITE_DAC on remote drives. */ - access |= READ_CONTROL | WRITE_DAC; - - status = NtCreateFile (&fh, access, - win32_newpath.get_object_attr (attr, sec_none_nih), - &io, NULL, FILE_ATTRIBUTE_NORMAL, - FILE_SHARE_VALID_FLAGS, - isdevice ? FILE_OVERWRITE_IF : FILE_CREATE, - FILE_SYNCHRONOUS_IO_NONALERT - | FILE_NON_DIRECTORY_FILE - | FILE_OPEN_FOR_BACKUP_INTENT, - NULL, 0); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - goto done; - } - if (win32_newpath.has_acls ()) - set_file_attribute (fh, win32_newpath, ILLEGAL_UID, ILLEGAL_GID, - (io.Information == FILE_CREATED ? S_JUSTCREATED : 0) - | S_IFLNK | STD_RBITS | STD_WBITS); - status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf, NULL, NULL); - if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf)) - { - status = NtSetAttributesFile (fh, wsym_type == WSYM_lnk - ? FILE_ATTRIBUTE_READONLY - : FILE_ATTRIBUTE_SYSTEM); - if (!NT_SUCCESS (status)) - debug_printf ("Setting attributes failed, status = %y", status); - res = 0; - } - else - { - __seterrno_from_nt_status (status); - FILE_DISPOSITION_INFORMATION fdi = { TRUE }; - status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi, - FileDispositionInformation); - if (!NT_SUCCESS (status)) - debug_printf ("Setting delete dispostion failed, status = %y", status); - } - NtClose (fh); + NtClose (fh); -done: + } + __except (EFAULT) {} + __endtry syscall_printf ("%d = symlink_worker(%s, %s, %d)", res, oldpath, newpath, isdevice); if (has_trailing_dirsep) @@ -3109,13 +3132,16 @@ extern "C" 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); + + __try + { + if (ulen == 0 && buf) + set_errno (EINVAL); + else + res = cygheap->cwd.get (buf, 1, 1, ulen); + } + __except (EFAULT) {} + __endtry return res; } @@ -3150,58 +3176,64 @@ get_current_dir_name (void) extern "C" int chdir (const char *in_dir) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (!*in_dir) + int res = -1; + + __try { - set_errno (ENOENT); - return -1; - } + if (!*in_dir) + { + set_errno (ENOENT); + __leave; + } - syscall_printf ("dir '%s'", in_dir); + 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; - } + /* 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); + __leave; + } - int res = -1; - const char *posix_cwd = NULL; - dev_t devn = path.get_device (); - if (!path.exists ()) - set_errno (ENOENT); - else if (!path.isdir ()) - set_errno (ENOTDIR); - else if (!isvirtual_dev (devn)) + const char *posix_cwd = NULL; + dev_t devn = path.get_device (); + if (!path.exists ()) + set_errno (ENOENT); + else if (!path.isdir ()) + set_errno (ENOTDIR); + else if (!isvirtual_dev (devn)) + { + /* 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; + } + else + { + posix_cwd = path.normalized_path; + res = 0; + } + + if (!res) + res = cygheap->cwd.set (&path, posix_cwd); + + /* Note that we're accessing cwd.posix without a lock here. + I didn't think it was worth locking just for strace. */ + syscall_printf ("%R = chdir() cygheap->cwd.posix '%s' native '%S'", res, + cygheap->cwd.get_posix (), path.get_nt_native_path ()); + } + __except (EFAULT) { - /* 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; + res = -1; } - else - { - posix_cwd = path.normalized_path; - res = 0; - } - - if (!res) - res = cygheap->cwd.set (&path, posix_cwd); - - /* Note that we're accessing cwd.posix without a lock here. I didn't think - it was worth locking just for strace. */ - syscall_printf ("%R = chdir() cygheap->cwd.posix '%s' native '%S'", res, - cygheap->cwd.get_posix (), path.get_nt_native_path ()); + __endtry MALLOC_CHECK; return res; } @@ -3239,10 +3271,6 @@ cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to, size_t size) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - path_conv p; size_t lsiz = 0; char *buf = NULL; @@ -3250,152 +3278,177 @@ cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to, int error = 0; bool relative = !!(what & CCP_RELATIVE); what &= CCP_CONVTYPE_MASK; + int ret = -1; - if (!from) + __try { - set_errno (EINVAL); - return -1; - } + if (!from) + { + set_errno (EINVAL); + __leave; + } - switch (what) - { - case CCP_POSIX_TO_WIN_A: - { - p.check ((const char *) from, - PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP - | PC_NO_ACCESS_CHECK | PC_NOWARN | (relative ? PC_NOFULL : 0)); - if (p.error) - return_with_errno (p.error); - PUNICODE_STRING up = p.get_nt_native_path (); - buf = tp.c_get (); - sys_wcstombs (buf, NT_MAX_PATH, - up->Buffer, up->Length / sizeof (WCHAR)); - /* Convert native path to standard DOS path. */ - if (!strncmp (buf, "\\??\\", 4)) - { - buf += 4; - if (buf[1] != ':') /* native UNC path */ - *(buf += 2) = '\\'; - } - else if (*buf == '\\') + switch (what) + { + case CCP_POSIX_TO_WIN_A: { - /* Device name points to somewhere else in the NT namespace. - Use GLOBALROOT prefix to convert to Win32 path. */ - char *p = buf + sys_wcstombs (buf, NT_MAX_PATH, - ro_u_globalroot.Buffer, - ro_u_globalroot.Length - / sizeof (WCHAR)); - sys_wcstombs (p, NT_MAX_PATH - (p - buf), + p.check ((const char *) from, + PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP + | PC_NO_ACCESS_CHECK | PC_NOWARN | (relative ? PC_NOFULL : 0)); + if (p.error) + { + set_errno (p.error); + __leave; + } + PUNICODE_STRING up = p.get_nt_native_path (); + buf = tp.c_get (); + sys_wcstombs (buf, NT_MAX_PATH, up->Buffer, up->Length / sizeof (WCHAR)); + /* Convert native path to standard DOS path. */ + if (!strncmp (buf, "\\??\\", 4)) + { + buf += 4; + if (buf[1] != ':') /* native UNC path */ + *(buf += 2) = '\\'; + } + else if (*buf == '\\') + { + /* Device name points to somewhere else in the NT namespace. + Use GLOBALROOT prefix to convert to Win32 path. */ + char *p = buf + sys_wcstombs (buf, NT_MAX_PATH, + ro_u_globalroot.Buffer, + ro_u_globalroot.Length + / sizeof (WCHAR)); + sys_wcstombs (p, NT_MAX_PATH - (p - buf), + up->Buffer, up->Length / sizeof (WCHAR)); + } + lsiz = strlen (buf) + 1; + /* TODO: Incoming "." is a special case which leads to a trailing + backslash ".\\" in the Win32 path. That's a result of the + conversion in normalize_posix_path. This should not occur + so the below code is just a band-aid. */ + if (relative && !strcmp ((const char *) from, ".") + && !strcmp (buf, ".\\")) + { + lsiz = 2; + buf[1] = '\0'; + } } - lsiz = strlen (buf) + 1; - /* TODO: Incoming "." is a special case which leads to a trailing - backslash ".\\" in the Win32 path. That's a result of the - conversion in normalize_posix_path. This should not occur - so the below code is just a band-aid. */ - if (relative && !strcmp ((const char *) from, ".") - && !strcmp (buf, ".\\")) - { - lsiz = 2; - buf[1] = '\0'; - } - } - break; - case CCP_POSIX_TO_WIN_W: - p.check ((const char *) from, - PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP - | PC_NO_ACCESS_CHECK | PC_NOWARN | (relative ? PC_NOFULL : 0)); - if (p.error) - return_with_errno (p.error); - /* Relative Windows paths are always restricted to MAX_PATH chars. */ - if (relative && !isabspath (p.get_win32 ()) - && sys_mbstowcs (NULL, 0, p.get_win32 ()) > MAX_PATH) - { - /* Recreate as absolute path. */ - p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW - | PC_NO_ACCESS_CHECK | PC_NOWARN); + break; + case CCP_POSIX_TO_WIN_W: + p.check ((const char *) from, + PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP + | PC_NO_ACCESS_CHECK | PC_NOWARN | (relative ? PC_NOFULL : 0)); if (p.error) - return_with_errno (p.error); - } - lsiz = p.get_wide_win32_path_len () + 1; - path = p.get_nt_native_path ()->Buffer; + { + set_errno (p.error); + __leave; + } + /* Relative Windows paths are always restricted to MAX_PATH chars. */ + if (relative && !isabspath (p.get_win32 ()) + && sys_mbstowcs (NULL, 0, p.get_win32 ()) > MAX_PATH) + { + /* Recreate as absolute path. */ + p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW + | PC_NO_ACCESS_CHECK | PC_NOWARN); + if (p.error) + { + set_errno (p.error); + __leave; + } + } + lsiz = p.get_wide_win32_path_len () + 1; + path = p.get_nt_native_path ()->Buffer; - /* Convert native path to standard DOS path. */ - if (!wcsncmp (path, L"\\??\\", 4)) - { - path[1] = L'\\'; - - /* Drop long path prefix for short pathnames. Unfortunately there's - quite a bunch of Win32 functions, especially in user32.dll, - apparently, which don't grok long path names at all, not even - in the UNICODE API. */ - if ((path[5] == L':' && lsiz <= MAX_PATH + 4) - || (!wcsncmp (path + 4, L"UNC\\", 4) && lsiz <= MAX_PATH + 6)) + /* Convert native path to standard DOS path. */ + if (!wcsncmp (path, L"\\??\\", 4)) { - path += 4; - lsiz -= 4; - if (path[1] != L':') + path[1] = L'\\'; + + /* Drop long path prefix for short pathnames. Unfortunately there's + quite a bunch of Win32 functions, especially in user32.dll, + apparently, which don't grok long path names at all, not even + in the UNICODE API. */ + if ((path[5] == L':' && lsiz <= MAX_PATH + 4) + || (!wcsncmp (path + 4, L"UNC\\", 4) && lsiz <= MAX_PATH + 6)) { - *(path += 2) = '\\'; - lsiz -= 2; + path += 4; + lsiz -= 4; + if (path[1] != L':') + { + *(path += 2) = '\\'; + lsiz -= 2; + } } } + else if (*path == L'\\') + { + /* Device name points to somewhere else in the NT namespace. + Use GLOBALROOT prefix to convert to Win32 path. */ + to = (void *) wcpcpy ((wchar_t *) to, ro_u_globalroot.Buffer); + lsiz += ro_u_globalroot.Length / sizeof (WCHAR); + } + /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */ + if (relative && !strcmp ((const char *) from, ".") + && !wcscmp (path, L".\\")) + { + lsiz = 2; + path[1] = L'\0'; + } + lsiz *= sizeof (WCHAR); + break; + case CCP_WIN_A_TO_POSIX: + buf = tp.c_get (); + error = mount_table->conv_to_posix_path ((const char *) from, buf, + relative); + if (error) + { + set_errno (p.error); + __leave; + } + lsiz = strlen (buf) + 1; + break; + case CCP_WIN_W_TO_POSIX: + buf = tp.c_get (); + error = mount_table->conv_to_posix_path ((const PWCHAR) from, buf, + relative); + if (error) + { + set_errno (error); + __leave; + } + lsiz = strlen (buf) + 1; + break; + default: + set_errno (EINVAL); + __leave; } - else if (*path == L'\\') + if (!size) { - /* Device name points to somewhere else in the NT namespace. - Use GLOBALROOT prefix to convert to Win32 path. */ - to = (void *) wcpcpy ((wchar_t *) to, ro_u_globalroot.Buffer); - lsiz += ro_u_globalroot.Length / sizeof (WCHAR); + ret = lsiz; + __leave; } - /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */ - if (relative && !strcmp ((const char *) from, ".") - && !wcscmp (path, L".\\")) + if (size < lsiz) { - lsiz = 2; - path[1] = L'\0'; + set_errno (ENOSPC); + __leave; } - lsiz *= sizeof (WCHAR); - break; - case CCP_WIN_A_TO_POSIX: - buf = tp.c_get (); - error = mount_table->conv_to_posix_path ((const char *) from, buf, - relative); - if (error) - return_with_errno (error); - lsiz = strlen (buf) + 1; - break; - case CCP_WIN_W_TO_POSIX: - buf = tp.c_get (); - error = mount_table->conv_to_posix_path ((const PWCHAR) from, buf, - relative); - if (error) - return_with_errno (error); - lsiz = strlen (buf) + 1; - break; - default: - set_errno (EINVAL); - return -1; - } - if (!size) - return lsiz; - if (size < lsiz) - { - set_errno (ENOSPC); - return -1; - } - switch (what) - { - case CCP_POSIX_TO_WIN_A: - case CCP_WIN_A_TO_POSIX: - case CCP_WIN_W_TO_POSIX: - stpcpy ((char *) to, buf); - break; - case CCP_POSIX_TO_WIN_W: - wcpcpy ((PWCHAR) to, path); - break; + switch (what) + { + case CCP_POSIX_TO_WIN_A: + case CCP_WIN_A_TO_POSIX: + case CCP_WIN_W_TO_POSIX: + stpcpy ((char *) to, buf); + break; + case CCP_POSIX_TO_WIN_W: + wcpcpy ((PWCHAR) to, path); + break; + } + ret = 0; } - return 0; + __except (EFAULT) {} + __endtry + return ret; } extern "C" void * @@ -3454,6 +3507,9 @@ cygwin_conv_to_full_posix_path (const char *path, char *posix_path) extern "C" char * realpath (const char *__restrict path, char *__restrict resolved) { + tmp_pathbuf tp; + char *tpath; + /* Make sure the right errno is returned if path is NULL. */ if (!path) { @@ -3463,48 +3519,48 @@ realpath (const char *__restrict path, char *__restrict resolved) /* Guard reading from a potentially invalid path and writing to a potentially invalid resolved. */ - tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; - - /* Win32 drive letter paths have to be converted to a POSIX path first, - because path_conv leaves the incoming path untouched except for - converting backslashes to forward slashes. */ - char *tpath; - if (isdrive (path)) + __try { - tpath = tp.c_get (); - mount_table->conv_to_posix_path (path, tpath, 0); - } - else - tpath = (char *) path; + /* Win32 drive letter paths have to be converted to a POSIX path first, + because path_conv leaves the incoming path untouched except for + converting backslashes to forward slashes. */ + if (isdrive (path)) + { + tpath = tp.c_get (); + mount_table->conv_to_posix_path (path, tpath, 0); + } + else + tpath = (char *) path; - path_conv real_path (tpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes); + path_conv real_path (tpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes); - /* POSIX 2008 requires malloc'ing if resolved is NULL, and states - that using non-NULL resolved is asking for portability - problems. */ + /* POSIX 2008 requires malloc'ing if resolved is NULL, and states + that using non-NULL resolved is asking for portability + problems. */ - if (!real_path.error && real_path.exists ()) - { - if (!resolved) + if (!real_path.error && real_path.exists ()) { - resolved = (char *) malloc (strlen (real_path.normalized_path) + 1); if (!resolved) - return NULL; + { + resolved = (char *) + malloc (strlen (real_path.normalized_path) + 1); + if (!resolved) + return NULL; + } + strcpy (resolved, real_path.normalized_path); + return resolved; } - strcpy (resolved, real_path.normalized_path); - return resolved; - } - /* FIXME: on error, Linux puts the name of the path - component which could not be resolved into RESOLVED, but POSIX - does not require this. */ - if (resolved) - resolved[0] = '\0'; - set_errno (real_path.error ?: ENOENT); + /* FIXME: on error, Linux puts the name of the path + component which could not be resolved into RESOLVED, but POSIX + does not require this. */ + if (resolved) + resolved[0] = '\0'; + set_errno (real_path.error ?: ENOENT); + } + __except (EFAULT) {} + __endtry return NULL; } |