Esempio n. 1
0
        private static IEnumerable <string> InternalEnumerateFiles(
            string directoryName,
            string filePattern,
            string fromFilePath,
            SearchOption searchOption,
            bool followsymlink,
            CancellationToken cancellationToken)
        {
            Stack <string> folders = new Stack <string>();
            Stack <string> currentFolderSubFolders = new Stack <string>();

            folders.Push(directoryName);

            string[] pathSegList             = null;
            bool     passedContinuationToken = false;
            int      pathSegListIndex        = 0;

            if (null != fromFilePath)
            {
                pathSegList = fromFilePath.Split(new char[] { Path.DirectorySeparatorChar });
            }
            else
            {
                passedContinuationToken = true;
            }

            while (folders.Count > 0)
            {
                string folder = AppendDirectorySeparator(folders.Pop());

                Utils.CheckCancellation(cancellationToken);

                // Skip non-existent folders
                if (!LongPathDirectory.Exists(folder))
                {
#if DOTNET5_4
                    if (CrossPlatformHelpers.IsLinux)
                    {
                        if (!SymlinkedDirExists(folder))
                        {
                            continue;
                        }
                    }
                    else
                    {
                        continue;
                    }
#else
                    continue;
#endif
                }

#if CODE_ACCESS_SECURITY // Only accessible to fully trusted code in non-CAS models
                try
                {
                    CheckPathDiscoveryPermission(folder);
                }
                catch (SecurityException)
                {
                    // Ignore this folder if we have no right to discovery it.
                    continue;
                }
                catch (UnauthorizedAccessException)
                {
                    // Ignore this folder if we have no right to discovery it.
                    continue;
                }
#else // CODE_ACCESS_SECURITY
                try
                {
                    // In non-CAS scenarios, it's still important to check for folder accessibility
                    // since the OS can block access to some paths. Getting files from a location
                    // will force path discovery checks which will indicate whether or not the user
                    // is authorized to access the directory.
                    foreach (var fileItem in LongPathDirectory.EnumerateFileSystemEntries(folder, "*", SearchOption.TopDirectoryOnly))
                    {
                        // Just try to get the first item in directly to check whether has permission to access the directory.
                        break;
                    }
                }
                catch (SecurityException)
                {
                    // Ignore this folder if we have no right to discovery it.
                    continue;
                }
                catch (UnauthorizedAccessException)
                {
                    // Ignore this folder if we have no right to discovery it.
                    continue;
                }
                catch (Exception ex)
                {
                    throw new TransferException(string.Format(CultureInfo.CurrentCulture, Resources.EnumerateDirectoryException, folder), ex);
                }
#endif // CODE_ACCESS_SECURITY

                // Return all files contained directly in this folder (which occur after the continuationTokenFile)
                // Only consider the folder if the continuation token is already past or the continuation token may be passed by
                // a file within this directory (based on pathSegListIndex and the patSegList.Length)
                if (passedContinuationToken ||
                    (pathSegList.Length - 1 == pathSegListIndex))
                {
                    string continuationTokenFile = null;

                    if (!passedContinuationToken)
                    {
                        continuationTokenFile = pathSegList[pathSegListIndex];
                    }

                    // Load files directly under this folder.
                    foreach (var filePath in Utils.CatchException(() =>
                    {
                        return(LongPathDirectory.EnumerateFileSystemEntries(folder, filePattern, SearchOption.TopDirectoryOnly));
                    },
                                                                  (ex) =>
                    {
                        throw new TransferException(string.Format(CultureInfo.CurrentCulture, Resources.EnumerateDirectoryException, folder), ex);
                    }))
                    {
                        Utils.CheckCancellation(cancellationToken);

                        EnumerateFileEntryInfo fileEntryInfo = null;

                        try
                        {
#if DOTNET5_4
                            if (CrossPlatformHelpers.IsLinux)
                            {
                                fileEntryInfo = GetFileEntryInfo(filePath);
                            }
                            else
                            {
                                fileEntryInfo = new EnumerateFileEntryInfo()
                                {
                                    FileName       = LongPath.GetFileName(filePath),
                                    FileAttributes = LongPathFile.GetAttributes(filePath)
                                };
                            }
#else
                            fileEntryInfo = new EnumerateFileEntryInfo()
                            {
                                FileName       = LongPath.GetFileName(filePath),
                                FileAttributes = LongPathFile.GetAttributes(filePath)
                            };
#endif
                        }
                        // Cross-plat file system accessibility settings may cause exceptions while
                        // retrieving attributes from inaccessible paths. These paths shold be skipped.
                        catch (FileNotFoundException) { }
                        catch (IOException) { }
                        catch (UnauthorizedAccessException) { }
                        catch (Exception ex)
                        {
                            throw new TransferException(string.Format(CultureInfo.CurrentCulture, Resources.FailedToGetFileInfoException, filePath), ex);
                        }

                        if (null == fileEntryInfo)
                        {
                            continue;
                        }

                        if (FileAttributes.Directory != (fileEntryInfo.FileAttributes & FileAttributes.Directory))
                        {
                            if (passedContinuationToken)
                            {
                                yield return(LongPath.Combine(folder, fileEntryInfo.FileName));
                            }
                            else
                            {
                                if (CrossPlatformHelpers.IsLinux)
                                {
                                    if (!passedContinuationToken)
                                    {
                                        if (string.Equals(fileEntryInfo.FileName, continuationTokenFile, StringComparison.Ordinal))
                                        {
                                            passedContinuationToken = true;
                                        }
                                    }
                                    else
                                    {
                                        yield return(LongPath.Combine(folder, fileEntryInfo.FileName));
                                    }
                                }
                                else
                                {
                                    // Windows file system is case-insensitive; OSX and Linux are case-sensitive
                                    var comparison    = CrossPlatformHelpers.IsWindows ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
                                    int compareResult = string.Compare(fileEntryInfo.FileName, continuationTokenFile, comparison);
                                    if (compareResult < 0)
                                    {
                                        // Skip files prior to the continuation token file
                                        continue;
                                    }

                                    passedContinuationToken = true;

                                    if (compareResult > 0)
                                    {
                                        yield return(LongPath.Combine(folder, fileEntryInfo.FileName));
                                    }
                                }
                            }
                        }
                    }

                    // Passed folder which continuation token file is under,
                    // set passedContinuationToken to true.
                    passedContinuationToken = true;
                }

                // Next add sub-folders for processing
                if (SearchOption.AllDirectories == searchOption)
                {
                    string fromSubfolder  = null;
                    bool   passedSubfoler = passedContinuationToken;
                    if (!passedContinuationToken)
                    {
                        fromSubfolder = pathSegList[pathSegListIndex];
                        pathSegListIndex++;
                    }

                    // Add sub-folders.
                    foreach (var filePath in Utils.CatchException(() =>
                    {
                        return(LongPathDirectory.EnumerateFileSystemEntries(folder, "*", SearchOption.TopDirectoryOnly));
                    },
                                                                  (ex) =>
                    {
                        throw new TransferException(string.Format(CultureInfo.CurrentCulture, Resources.EnumerateDirectoryException, folder), ex);
                    }))
                    {
                        Utils.CheckCancellation(cancellationToken);

                        EnumerateFileEntryInfo fileEntryInfo = null;

                        try
                        {
                            if (CrossPlatformHelpers.IsLinux)
                            {
                                fileEntryInfo = GetFileEntryInfo(filePath);
                            }
                            else
                            {
                                fileEntryInfo = new EnumerateFileEntryInfo()
                                {
                                    FileName       = LongPath.GetFileName(filePath),
                                    FileAttributes = LongPathFile.GetAttributes(filePath)
                                };
                            }
                        }
                        // Cross-plat file system accessibility settings may cause exceptions while
                        // retrieving attributes from inaccessible paths. These paths shold be skipped.
                        catch (FileNotFoundException) { }
                        catch (IOException) { }
                        catch (UnauthorizedAccessException) { }
                        catch (Exception ex)
                        {
                            throw new TransferException(string.Format(CultureInfo.CurrentCulture, Resources.FailedToGetFileInfoException, filePath), ex);
                        }

                        if (null == fileEntryInfo)
                        {
                            continue;
                        }

                        if (FileAttributes.Directory == (fileEntryInfo.FileAttributes & FileAttributes.Directory) &&
                            !fileEntryInfo.FileName.Equals(@".") &&
                            !fileEntryInfo.FileName.Equals(@".."))
                        {
                            bool toBeEnumerated = false;
                            if (CrossPlatformHelpers.IsLinux)
                            {
                                toBeEnumerated = ToEnumerateTheSubDir(LongPath.Combine(folder, fileEntryInfo.FileName), fileEntryInfo, followsymlink);
                            }
                            // TODO: Ignore junction point or not. Make it configurable.
                            else if (FileAttributes.ReparsePoint != (fileEntryInfo.FileAttributes & FileAttributes.ReparsePoint))
                            {
                                toBeEnumerated = true;
                            }

                            if (toBeEnumerated)
                            {
                                if (passedSubfoler)
                                {
                                    currentFolderSubFolders.Push(LongPath.Combine(folder, fileEntryInfo.FileName));
                                }
                                else
                                {
                                    if (CrossPlatformHelpers.IsLinux)
                                    {
                                        if (string.Equals(fileEntryInfo.FileName, fromSubfolder, StringComparison.Ordinal))
                                        {
                                            passedSubfoler = true;
                                            currentFolderSubFolders.Push(LongPath.Combine(folder, fileEntryInfo.FileName));
                                        }
                                    }
                                    else
                                    {
                                        // Windows file system is case-insensitive; OSX and Linux are case-sensitive
                                        var comparison    = CrossPlatformHelpers.IsWindows ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
                                        int compareResult = string.Compare(fileEntryInfo.FileName, fromSubfolder, comparison);

                                        if (compareResult >= 0)
                                        {
                                            passedSubfoler = true;
                                            currentFolderSubFolders.Push(LongPath.Combine(folder, fileEntryInfo.FileName));

                                            if (compareResult > 0)
                                            {
                                                passedContinuationToken = true;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    if (currentFolderSubFolders.Count <= 0)
                    {
                        passedContinuationToken = true;
                    }
                    else
                    {
                        while (currentFolderSubFolders.Count > 0)
                        {
                            folders.Push(currentFolderSubFolders.Pop());
                        }
                    }
                }
            }
        }