Esempio n. 1
0
        private CreateHardLinkResult CreateHardLinkWin(AbsolutePath sourceFileName, AbsolutePath destinationFileName, bool replaceExisting)
        {
            SafeFileHandle sourceFileHandle = NativeMethods.CreateFile(
                sourceFileName.Path,
                0,
                /* Do not need to request any particular access to modify link info */ FileShare.ReadWrite | FileShare.Delete,
                IntPtr.Zero,
                FileMode.Open,
                0 /* Allow symbolic links to redirect us */,
                IntPtr.Zero);

            using (sourceFileHandle)
            {
                if (sourceFileHandle.IsInvalid)
                {
                    switch (Marshal.GetLastWin32Error())
                    {
                    case NativeMethods.ERROR_FILE_NOT_FOUND:
                    case NativeMethods.ERROR_PATH_NOT_FOUND:
                        return(CreateHardLinkResult.FailedSourceDoesNotExist);

                    case NativeMethods.ERROR_ACCESS_DENIED:
                        return(CreateHardLinkResult.FailedSourceAccessDenied);

                    default:
                        return(CreateHardLinkResult.FailedSourceHandleInvalid);
                    }
                }

                if (destinationFileName.Length >= FileSystemConstants.MaxPath)
                {
                    return(CreateHardLinkResult.FailedPathTooLong);
                }

                const string DosToNtPathPrefix = @"\??\";

                // NtSetInformationFile always expects a special prefix even for short paths.
                string path = DosToNtPathPrefix + destinationFileName.GetPathWithoutLongPathPrefix();

                var linkInfo = new NativeMethods.FileLinkInformation(path, replaceExisting);
                NativeMethods.NtStatus status = setLink(sourceFileHandle, linkInfo);

                if (status.StatusCodeUint == (uint)NativeMethods.NtStatusCode.StatusAccessDenied)
                {
                    // Access denied status can be returned by two reasons:
                    // 1. Something went wrong with the source path
                    // 2. Something went wrong with the destination path.

                    var retry = false;

                    // For case 1: we'll make sure that the source file allows attribute writes.
                    if (!FileUtilities.HasWritableAttributeAccessControl(sourceFileName.Path))
                    {
                        AllowAttributeWrites(sourceFileName);
                        retry = true;
                    }

                    // For case 2: we'll check the destination's file attribute
                    // and if the file has readonly attributes, then we'll remove them and will try to create hardlink one more time.
                    if (this.TryGetFileAttributes(destinationFileName, out var attributes) && (attributes & FileAttributes.ReadOnly) != 0)
                    {
                        SetFileAttributes(destinationFileName, FileAttributes.Normal);
                        retry = true;
                    }

                    if (retry)
                    {
                        status = setLink(sourceFileHandle, linkInfo);
                    }
                }

                if (status.Failed)
                {
                    switch (status.StatusCodeUint)
                    {
                    case (uint)NativeMethods.NtStatusCode.StatusTooManyLinks:
                        return(CreateHardLinkResult.FailedMaxHardLinkLimitReached);

                    case (uint)NativeMethods.NtStatusCode.StatusObjectNameCollision:
                        return(CreateHardLinkResult.FailedDestinationExists);

                    case (uint)NativeMethods.NtStatusCode.StatusNotSameDevice:
                        return(CreateHardLinkResult.FailedSourceAndDestinationOnDifferentVolumes);

                    case (uint)NativeMethods.NtStatusCode.StatusAccessDenied:
                        return(CreateHardLinkResult.FailedAccessDenied);

                    case (uint)NativeMethods.NtStatusCode.StatusNotSupported:
                        return(CreateHardLinkResult.FailedNotSupported);

                    case (uint)NativeMethods.NtStatusCode.StatusObjectPathNotFound:
                        return(CreateHardLinkResult.FailedDestinationDirectoryDoesNotExist);

                    default:
                        throw new NTStatusException(status.StatusCodeUint, status.StatusName, string.Format(
                                                        CultureInfo.InvariantCulture,
                                                        "Unable to create hard link at '{0}', pointing to existing file '{1}' with NTSTATUS:[0x{2:X}] = [{3}]",
                                                        destinationFileName,
                                                        sourceFileName,
                                                        status.StatusCodeUint,
                                                        status.StatusName));
                    }
                }

                return(CreateHardLinkResult.Success);
            }
Esempio n. 2
0
        private CreateHardLinkResult CreateHardLinkWin(AbsolutePath sourceFileName, AbsolutePath destinationFileName, bool replaceExisting)
        {
            SafeFileHandle sourceFileHandle = NativeMethods.CreateFile(
                sourceFileName.Path,
                0,
                /* Do not need to request any particular access to modify link info */ FileShare.ReadWrite | FileShare.Delete,
                IntPtr.Zero,
                FileMode.Open,
                0 /* Allow symbolic links to redirect us */,
                IntPtr.Zero);

            using (sourceFileHandle)
            {
                if (sourceFileHandle.IsInvalid)
                {
                    switch (Marshal.GetLastWin32Error())
                    {
                    case NativeMethods.ERROR_FILE_NOT_FOUND:
                    case NativeMethods.ERROR_PATH_NOT_FOUND:
                        return(CreateHardLinkResult.FailedSourceDoesNotExist);

                    case NativeMethods.ERROR_ACCESS_DENIED:
                        return(CreateHardLinkResult.FailedSourceAccessDenied);

                    default:
                        return(CreateHardLinkResult.FailedSourceHandleInvalid);
                    }
                }

                if (destinationFileName.Length >= FileSystemConstants.MaxPath)
                {
                    return(CreateHardLinkResult.FailedPathTooLong);
                }

                const string DosToNtPathPrefix = @"\??\";

                // NtSetInformationFile always expects a special prefix even for short paths.
                string path = DosToNtPathPrefix + destinationFileName.GetPathWithoutLongPathPrefix();

                var linkInfo = new NativeMethods.FileLinkInformation(path, replaceExisting);
                NativeMethods.NtStatus status = setLink(sourceFileHandle, linkInfo);

                if (status.StatusCodeUint == (uint)NativeMethods.NtStatusCode.StatusAccessDenied)
                {
                    if ((GetFileAttributes(destinationFileName) & FileAttributes.ReadOnly) != 0)
                    {
                        SetFileAttributes(destinationFileName, FileAttributes.Normal);
                        status = setLink(sourceFileHandle, linkInfo);
                    }
                }

                if (status.Failed)
                {
                    switch (status.StatusCodeUint)
                    {
                    case (uint)NativeMethods.NtStatusCode.StatusTooManyLinks:
                        return(CreateHardLinkResult.FailedMaxHardLinkLimitReached);

                    case (uint)NativeMethods.NtStatusCode.StatusObjectNameCollision:
                        return(CreateHardLinkResult.FailedDestinationExists);

                    case (uint)NativeMethods.NtStatusCode.StatusNotSameDevice:
                        return(CreateHardLinkResult.FailedSourceAndDestinationOnDifferentVolumes);

                    case (uint)NativeMethods.NtStatusCode.StatusAccessDenied:
                        return(CreateHardLinkResult.FailedAccessDenied);

                    case (uint)NativeMethods.NtStatusCode.StatusNotSupported:
                        return(CreateHardLinkResult.FailedNotSupported);

                    case (uint)NativeMethods.NtStatusCode.StatusObjectPathNotFound:
                        return(CreateHardLinkResult.FailedDestinationDirectoryDoesNotExist);

                    default:
                        throw new NTStatusException(status.StatusCodeUint, status.StatusName, string.Format(
                                                        CultureInfo.InvariantCulture,
                                                        "Unable to create hard link at '{0}', pointing to existing file '{1}' with NTSTATUS:[0x{2:X}] = [{3}]",
                                                        destinationFileName,
                                                        sourceFileName,
                                                        status.StatusCodeUint,
                                                        status.StatusName));
                    }
                }

                return(CreateHardLinkResult.Success);
            }

            NativeMethods.NtStatus setLink(SafeFileHandle handle, NativeMethods.FileLinkInformation linkInfo)
            {
                return(NativeMethods.NtSetInformationFile(
                           handle,
                           out _,
                           linkInfo,
                           (uint)Marshal.SizeOf(linkInfo),
                           NativeMethods.FileInformationClass.FileLinkInformation));
            }
        }