diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 5c5cc537c..aa47a9064 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,22 @@ +2005-02-19 Corinna Vinschen + + * fhandler.h (class fhandler_base): Declare new method link. + (class fhandler_socket): Ditto. + (class fhandler_disk_file): Ditto. + * fhandler.cc (fhandler_base::open): Add FILE_WRITE_ATTRIBUTES + to query_write_control access flags. + (fhandler_base::link): New method. + * fhandler_disk_file.cc (fhandler_disk_file::fchmod): Don't try to + open with O_WRONLY since query_write_control includes + FILE_WRITE_ATTRIBUTES. + (fhandler_disk_file::fchown): Ditto. + (fhandler_disk_file::facl): Ditto. + (fhandler_disk_file::link): New method. Touch st_ctime on successful + link. + * fhandler_socket.cc (fhandler_socket::link): New method. + * syscalls.cc (link): Move functionality into fhandler method link. + Just call this method from here. + 2005-02-19 Corinna Vinschen * fhandler.h (class fhandler_socket): Declare new methods fchown, diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc index 3fd0bc74b..f846d044e 100644 --- a/winsup/cygwin/fhandler.cc +++ b/winsup/cygwin/fhandler.cc @@ -580,7 +580,7 @@ fhandler_base::open (int flags, mode_t mode) create_options = FILE_OPEN_FOR_BACKUP_INTENT; break; case query_write_control: - access = READ_CONTROL | WRITE_OWNER | WRITE_DAC; + access = READ_CONTROL | WRITE_OWNER | WRITE_DAC | FILE_WRITE_ATTRIBUTES; create_options = FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_FOR_RECOVERY; break; default: @@ -1605,3 +1605,10 @@ fhandler_base::ftruncate (_off64_t length) set_errno (EINVAL); return -1; } + +int +fhandler_base::link (const char *newpath) +{ + set_errno (EINVAL); + return -1; +} diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 0617a82c5..591c08894 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -264,6 +264,7 @@ class fhandler_base virtual int __stdcall fchown (__uid32_t uid, __gid32_t gid) __attribute__ ((regparm (2))); virtual int __stdcall facl (int, int, __acl32 *) __attribute__ ((regparm (3))); virtual int __stdcall ftruncate (_off64_t) __attribute__ ((regparm (2))); + virtual int __stdcall link (const char *) __attribute__ ((regparm (2))); virtual int ioctl (unsigned int cmd, void *); virtual int fcntl (int cmd, void *); virtual char const *ttyname () { return get_name (); } @@ -436,6 +437,7 @@ class fhandler_socket: public fhandler_base int __stdcall fchmod (mode_t mode) __attribute__ ((regparm (1))); int __stdcall fchown (__uid32_t uid, __gid32_t gid) __attribute__ ((regparm (2))); int __stdcall facl (int, int, __acl32 *) __attribute__ ((regparm (3))); + int __stdcall link (const char *) __attribute__ ((regparm (2))); bool is_slow () {return 1;} }; @@ -608,6 +610,7 @@ class fhandler_disk_file: public fhandler_base int __stdcall fchown (__uid32_t uid, __gid32_t gid) __attribute__ ((regparm (2))); int __stdcall facl (int, int, __acl32 *) __attribute__ ((regparm (3))); int __stdcall ftruncate (_off64_t) __attribute__ ((regparm (2))); + int __stdcall link (const char *) __attribute__ ((regparm (2))); HANDLE mmap (caddr_t *addr, size_t len, DWORD access, int flags, _off64_t off); int munmap (HANDLE h, caddr_t addr, size_t len); diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc index 8644e8964..b77ae82fd 100644 --- a/winsup/cygwin/fhandler_disk_file.cc +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -408,13 +408,9 @@ fhandler_disk_file::fchmod (mode_t mode) enable_restore_privilege (); if (!get_io_handle () && pc.has_acls ()) { - /* Open for writing required to be able to set ctime. */ - if (!(oret = open (O_WRONLY | O_BINARY, 0))) - { - query_open (query_write_control); - if (!(oret = open (O_BINARY, 0))) - return -1; - } + query_open (query_write_control); + if (!(oret = open (O_BINARY, 0))) + return -1; } if (!allow_ntsec && allow_ntea) /* Not necessary when manipulating SD. */ @@ -440,7 +436,7 @@ fhandler_disk_file::fchmod (mode_t mode) res = 0; /* Set ctime on success. */ - if (!res && !query_open ()) + if (!res) has_changed (true); if (oret) @@ -464,13 +460,9 @@ fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid) enable_restore_privilege (); if (!get_io_handle ()) { - /* Open for writing required to be able to set ctime. */ - if (!(oret = open (O_WRONLY | O_BINARY, 0))) - { - query_open (query_write_control); - if (!(oret = open (O_BINARY, 0))) - return -1; - } + query_open (query_write_control); + if (!(oret = open (O_BINARY, 0))) + return -1; } mode_t attrib = 0; @@ -482,7 +474,7 @@ fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid) res = set_file_attribute (pc.has_acls (), get_io_handle (), pc, uid, gid, attrib); /* Set ctime on success. */ - if (!res && !query_open ()) + if (!res) has_changed (true); } @@ -556,16 +548,9 @@ fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp) enable_restore_privilege (); if (!get_io_handle ()) { - /* Open for writing required to be able to set ctime. */ - if (cmd == SETACL) - oret = open (O_WRONLY | O_BINARY, 0); - if (!oret) - { - query_open (cmd == SETACL ? query_write_control - : query_read_control); - if (!(oret = open (O_BINARY, 0))) - return -1; - } + query_open (cmd == SETACL ? query_write_control : query_read_control); + if (!(oret = open (O_BINARY, 0))) + return -1; } switch (cmd) { @@ -588,7 +573,7 @@ fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp) } /* Set ctime on success. */ - if (!res && cmd == SETACL && !query_open ()) + if (!res && cmd == SETACL) has_changed (true); if (oret) @@ -645,6 +630,173 @@ fhandler_disk_file::ftruncate (_off64_t length) return res; } +int +fhandler_disk_file::link (const char *newpath) +{ + int res = -1; + path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_FULL | PC_POSIX); + extern bool allow_winsymlinks; + + if (newpc.error) + { + set_errno (newpc.case_clash ? ECASECLASH : newpc.error); + goto done; + } + + if (newpc.exists ()) + { + syscall_printf ("file '%s' exists?", (char *) newpc); + set_errno (EEXIST); + goto done; + } + + if (newpc[strlen (newpc) - 1] == '.') + { + syscall_printf ("trailing dot, bailing out"); + set_errno (EINVAL); + goto done; + } + + /* Shortcut hack. */ + char new_lnk_buf[CYG_MAX_PATH + 5]; + if (allow_winsymlinks && pc.is_lnk_symlink () && !newpc.case_clash) + { + strcpy (new_lnk_buf, newpath); + strcat (new_lnk_buf, ".lnk"); + newpath = new_lnk_buf; + newpc.check (newpath, PC_SYM_NOFOLLOW | PC_FULL); + } + + query_open (query_write_control); + if (!open (O_BINARY, 0)) + { + syscall_printf ("Opening file failed"); + __seterrno (); + goto done; + } + + /* Try to make hard link first on Windows NT */ + if (wincap.has_hard_links ()) + { + if (CreateHardLinkA (newpc, pc, NULL)) + goto success; + + /* There are two cases to consider: + - The FS doesn't support hard links ==> ERROR_INVALID_FUNCTION + We copy the file. + - CreateHardLinkA is not supported ==> ERROR_PROC_NOT_FOUND + In that case (<= NT4) we try the old-style method. + Any other error should be taken seriously. */ + if (GetLastError () == ERROR_INVALID_FUNCTION) + { + syscall_printf ("FS doesn't support hard links: Copy file"); + goto docopy; + } + if (GetLastError () != ERROR_PROC_NOT_FOUND) + { + syscall_printf ("CreateHardLinkA failed"); + __seterrno (); + close (); + goto done; + } + + WIN32_STREAM_ID stream_id; + DWORD written; + LPVOID context; + DWORD path_len; + DWORD size; + WCHAR wbuf[CYG_MAX_PATH]; + BOOL ret; + DWORD write_err; + + path_len = sys_mbstowcs (wbuf, newpc, CYG_MAX_PATH) * sizeof (WCHAR); + + stream_id.dwStreamId = BACKUP_LINK; + stream_id.dwStreamAttributes = 0; + stream_id.dwStreamNameSize = 0; + stream_id.Size.HighPart = 0; + stream_id.Size.LowPart = path_len; + size = sizeof (WIN32_STREAM_ID) - sizeof (WCHAR**) + + stream_id.dwStreamNameSize; + context = NULL; + write_err = 0; + /* Write WIN32_STREAM_ID */ + ret = BackupWrite (get_handle (), (LPBYTE) &stream_id, size, + &written, FALSE, FALSE, &context); + if (ret) + { + /* write the buffer containing the path */ + /* FIXME: BackupWrite sometimes traps if linkname is invalid. + Need to handle. */ + ret = BackupWrite (get_handle (), (LPBYTE) wbuf, path_len, + &written, FALSE, FALSE, &context); + if (!ret) + { + write_err = GetLastError (); + syscall_printf ("cannot write linkname, %E"); + } + /* Free context */ + BackupWrite (get_handle (), NULL, 0, &written, + TRUE, FALSE, &context); + } + else + { + write_err = GetLastError (); + syscall_printf ("cannot write stream_id, %E"); + } + + if (!ret) + { + /* Only copy file if FS doesn't support hard links */ + if (write_err == ERROR_INVALID_FUNCTION) + { + syscall_printf ("FS doesn't support hard links: Copy file"); + goto docopy; + } + + close (); + __seterrno_from_win_error (write_err); + goto done; + } + + success: + res = 0; + /* touch st_ctime */ + has_changed (true); + close (); + if (!allow_winsymlinks && pc.is_lnk_symlink ()) + SetFileAttributes (newpc, (DWORD) pc + | FILE_ATTRIBUTE_SYSTEM + | FILE_ATTRIBUTE_READONLY); + + goto done; + } +docopy: + /* do this with a copy */ + if (CopyFileA (pc, newpc, 1)) + { + res = 0; + /* touch st_ctime */ + has_changed (true); + close (); + fhandler_disk_file fh; + fh.set_name (newpc); + fh.query_open (query_write_control); + if (fh.open (O_BINARY, 0)) + { + fh.has_changed (true); + fh.close (); + } + } + else + __seterrno (); + +done: + syscall_printf ("%d = link (%s, %s)", res, get_name (), newpath); + return res; +} + + fhandler_disk_file::fhandler_disk_file () : fhandler_base () { diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index 757c5a76b..7038243e1 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -448,6 +448,18 @@ fhandler_socket::facl (int cmd, int nentries, __aclent32_t *aclbufp) return fhandler_base::facl (cmd, nentries, aclbufp); } +int +fhandler_socket::link (const char *newpath) +{ + if (get_device () == FH_UNIX) + { + fhandler_disk_file fh; + fh.set_name (pc); + return fh.link (newpath); + } + return fhandler_base::link (newpath); +} + int fhandler_socket::bind (const struct sockaddr *name, int namelen) { diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 18ef7ea00..079775657 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -668,188 +668,25 @@ isatty (int fd) */ extern "C" int -link (const char *a, const char *b) +link (const char *oldpath, const char *newpath) { int res = -1; - path_conv real_a (a, PC_SYM_NOFOLLOW | PC_FULL); - path_conv real_b (b, PC_SYM_NOFOLLOW | PC_FULL); - extern bool allow_winsymlinks; + fhandler_base *fh; - if (real_a.error) + if (!(fh = build_fh_name (oldpath, NULL, PC_SYM_NOFOLLOW))) + goto error; + + if (fh->error ()) { - set_errno (real_a.error); - goto done; + debug_printf ("got %d error from build_fh_name", fh->error ()); + set_errno (fh->error ()); } - - if (real_b.error) - { - set_errno (real_b.case_clash ? ECASECLASH : real_b.error); - goto done; - } - - if (real_b.exists ()) - { - syscall_printf ("file '%s' exists?", (char *) real_b); - set_errno (EEXIST); - goto done; - } - - if (real_b[strlen (real_b) - 1] == '.') - { - syscall_printf ("trailing dot, bailing out"); - set_errno (EINVAL); - goto done; - } - - /* Shortcut hack. */ - char new_lnk_buf[CYG_MAX_PATH + 5]; - if (allow_winsymlinks && real_a.is_lnk_symlink () && !real_b.case_clash) - { - strcpy (new_lnk_buf, b); - strcat (new_lnk_buf, ".lnk"); - b = new_lnk_buf; - real_b.check (b, PC_SYM_NOFOLLOW | PC_FULL); - } - /* Try to make hard link first on Windows NT */ - if (wincap.has_hard_links ()) - { - if (CreateHardLinkA (real_b, real_a, NULL)) - goto success; - - /* There are two cases to consider: - - The FS doesn't support hard links ==> ERROR_INVALID_FUNCTION - We copy the file. - - CreateHardLinkA is not supported ==> ERROR_PROC_NOT_FOUND - In that case (<= NT4) we try the old-style method. - Any other error should be taken seriously. */ - if (GetLastError () == ERROR_INVALID_FUNCTION) - { - syscall_printf ("FS doesn't support hard links: Copy file"); - goto docopy; - } - if (GetLastError () != ERROR_PROC_NOT_FOUND) - { - syscall_printf ("CreateHardLinkA failed"); - __seterrno (); - goto done; - } - - HANDLE hFileSource; - - WIN32_STREAM_ID StreamId; - DWORD dwBytesWritten; - LPVOID lpContext; - DWORD cbPathLen; - DWORD StreamSize; - WCHAR wbuf[CYG_MAX_PATH]; - - BOOL bSuccess; - DWORD write_err; - - hFileSource = CreateFile (real_a, FILE_WRITE_ATTRIBUTES, - FILE_SHARE_READ | FILE_SHARE_WRITE /*| FILE_SHARE_DELETE*/, - &sec_none_nih, // sa - OPEN_EXISTING, 0, NULL); - - if (hFileSource == INVALID_HANDLE_VALUE) - { - syscall_printf ("cannot open source, %E"); - goto docopy; - } - - cbPathLen = sys_mbstowcs (wbuf, real_b, CYG_MAX_PATH) * sizeof (WCHAR); - - StreamId.dwStreamId = BACKUP_LINK; - StreamId.dwStreamAttributes = 0; - StreamId.dwStreamNameSize = 0; - StreamId.Size.HighPart = 0; - StreamId.Size.LowPart = cbPathLen; - - StreamSize = sizeof (WIN32_STREAM_ID) - sizeof (WCHAR**) + - StreamId.dwStreamNameSize; - - lpContext = NULL; - write_err = 0; - /* Write the WIN32_STREAM_ID */ - bSuccess = BackupWrite ( - hFileSource, - (LPBYTE) &StreamId, // buffer to write - StreamSize, // number of bytes to write - &dwBytesWritten, - FALSE, // don't abort yet - FALSE, // don't process security - &lpContext); - - if (bSuccess) - { - /* write the buffer containing the path */ - /* FIXME: BackupWrite sometimes traps if linkname is invalid. - Need to handle. */ - bSuccess = BackupWrite ( - hFileSource, - (LPBYTE) wbuf, // buffer to write - cbPathLen, // number of bytes to write - &dwBytesWritten, - FALSE, // don't abort yet - FALSE, // don't process security - &lpContext - ); - - if (!bSuccess) - { - write_err = GetLastError (); - syscall_printf ("cannot write linkname, %E"); - } - - /* Free context */ - BackupWrite ( - hFileSource, - NULL, // buffer to write - 0, // number of bytes to write - &dwBytesWritten, - TRUE, // abort - FALSE, // don't process security - &lpContext); - } - else - { - write_err = GetLastError (); - syscall_printf ("cannot write streamId, %E"); - } - - CloseHandle (hFileSource); - - if (!bSuccess) - { - /* Only copy file if FS doesn't support hard links */ - if (write_err == ERROR_INVALID_FUNCTION) - { - syscall_printf ("FS doesn't support hard links: Copy file"); - goto docopy; - } - - __seterrno_from_win_error (write_err); - goto done; - } - - success: - res = 0; - if (!allow_winsymlinks && real_a.is_lnk_symlink ()) - SetFileAttributes (real_b, (DWORD) real_a - | FILE_ATTRIBUTE_SYSTEM - | FILE_ATTRIBUTE_READONLY); - - goto done; - } -docopy: - /* do this with a copy */ - if (CopyFileA (real_a, real_b, 1)) - res = 0; else - __seterrno (); + res = fh->link (newpath); -done: - syscall_printf ("%d = link (%s, %s)", res, a, b); + delete fh; + error: + syscall_printf ("%d = link (%s, %s)", res, oldpath, newpath); return res; }