示例#1
0
        private void MountPath(string path, IBlobStorage storage, bool isMountPoint)
        {
            string containerPath = StoragePath.IsRootPath(path) ? path : StoragePath.GetParent(path);

            if (!_pathToMountBlobs.TryGetValue(containerPath, out HashSet <Blob> blobs))
            {
                blobs = new HashSet <Blob>();
                _pathToMountBlobs[containerPath] = blobs;
            }

            // this is the mount
            if (isMountPoint)
            {
                var mountBlob = new Blob(path, BlobItemKind.Folder)
                {
                    Tag = storage
                };
                mountBlob.TryAddProperties("IsMountPoint", true);
                blobs.Add(mountBlob);
            }
            else
            {
                var intBlob = new Blob(path, BlobItemKind.Folder);
                blobs.Add(intBlob);
            }
        }
        protected override async Task <IReadOnlyCollection <Blob> > ListAtAsync(string path, ListOptions options, CancellationToken cancellationToken)
        {
            ObjectsResource.ListRequest request = _client.Service.Objects.List(_bucketName);
            request.Prefix    = StoragePath.IsRootPath(path) ? null : (NormalisePath(path) + "/");
            request.Delimiter = "/";

            var page = new List <Blob>();

            do
            {
                Objects serviceObjects = await request.ExecuteAsync(cancellationToken : cancellationToken).ConfigureAwait(false);

                if (serviceObjects.Items != null)
                {
                    page.AddRange(GConvert.ToBlobs(serviceObjects.Items, options));
                }

                if (serviceObjects.Prefixes != null)
                {
                    //the only info we have about prefixes is it's name
                    page.AddRange(serviceObjects.Prefixes.Select(p => new Blob(p, BlobItemKind.Folder)));
                }


                request.PageToken = serviceObjects.NextPageToken;
            }while(request.PageToken != null);

            return(page);
        }
示例#3
0
        private async Task <Blob> GetBlobAsync(string fullPath, CancellationToken cancellationToken)
        {
            DecomposePath(fullPath, out string fs, out string rp, false);

            if (StoragePath.IsRootPath(rp))
            {
                try
                {
                    ApiResponse <string> response = await _restApi.GetFilesystemProperties(fs);

                    await response.EnsureSuccessStatusCodeAsync();

                    var fsProps = new PathProperties(response);
                    return(LConvert.ToBlob(fs, fsProps));
                }
                catch (ApiException ex) when(ex.StatusCode == HttpStatusCode.NotFound)
                {
                    return(null);
                }
            }

            PathProperties pp;

            try
            {
                pp = await GetPathPropertiesAsync(fs, rp, "getProperties").ConfigureAwait(false);
            }
            catch (ApiException ex) when(ex.StatusCode == HttpStatusCode.NotFound)
            {
                return(null);
            }

            return(LConvert.ToBlob(fullPath, pp));
        }
示例#4
0
        protected virtual async Task DeleteAsync(string fullPath, CancellationToken cancellationToken)
        {
            (BlobContainerClient container, string path) = await GetPartsAsync(fullPath, false).ConfigureAwait(false);

            if (StoragePath.IsRootPath(path))
            {
                //deleting the entire container / filesystem
                await container.DeleteIfExistsAsync(cancellationToken : cancellationToken).ConfigureAwait(false);
            }
            else
            {
                BlockBlobClient blob = string.IsNullOrEmpty(path)
               ? null
               : container.GetBlockBlobClient(StoragePath.Normalize(path));
                if (blob != null)
                {
                    try
                    {
                        await blob.DeleteAsync(
                            DeleteSnapshotsOption.IncludeSnapshots, cancellationToken : cancellationToken).ConfigureAwait(false);
                    }
                    catch (RequestFailedException ex) when(ex.ErrorCode == "BlobNotFound")
                    {
                        //this might be a folder reference, just try it

                        await foreach (BlobItem recursedFile in
                                       container.GetBlobsAsync(prefix: path, cancellationToken: cancellationToken).ConfigureAwait(false))
                        {
                            BlobClient client = container.GetBlobClient(recursedFile.Name);
                            await client.DeleteIfExistsAsync(cancellationToken : cancellationToken).ConfigureAwait(false);
                        }
                    }
                }
            }
        }
        private async Task DeleteAsync(string fullPath, CancellationToken cancellationToken)
        {
            (CloudBlobContainer container, string path) = await GetPartsAsync(fullPath, false);

            if (StoragePath.IsRootPath(path))
            {
                //deleting the entire container
                await container.DeleteIfExistsAsync(cancellationToken).ConfigureAwait(false);
            }
            else
            {
                CloudBlockBlob blob = string.IsNullOrEmpty(path)
               ? null
               : container.GetBlockBlobReference(StoragePath.Normalize(path, false));
                if (blob != null && await blob.ExistsAsync().ConfigureAwait(false))
                {
                    await blob.DeleteIfExistsAsync(DeleteSnapshotsOption.IncludeSnapshots, null, null, null, cancellationToken);
                }
                else
                {
                    //try deleting as a folder
                    CloudBlobDirectory dir = container.GetDirectoryReference(StoragePath.Normalize(path, false));
                    using (var browser = new AzureBlobDirectoryBrowser(container, _containerName == null, 3))
                    {
                        await browser.RecursiveDeleteAsync(dir, cancellationToken).ConfigureAwait(false);
                    }
                }
            }
        }
示例#6
0
        public async Task RefreshBlobsAsync()
        {
            if (Storage == null)
            {
                return;
            }

            SingleClickNavigation = GlobalSettings.Default.SingleClickNavigation;

            IsLoading = true;

            try
            {
                List <Blob> items      = (await Storage.ListAsync(recurse: false, folderPath: FolderPath, includeAttributes: true)).ToList();
                var         finalItems = new List <Blob>(items.Count);
                if (GlobalSettings.Default.FoldersFirst)
                {
                    finalItems.AddRange(items.Where(i => i.IsFolder).OrderBy(f => f.Name));
                    finalItems.AddRange(items.Where(i => i.IsFile).OrderBy(f => f.Name));
                }
                else
                {
                    finalItems = items;
                }

                if (!StoragePath.IsRootPath(FolderPath))
                {
                    //can't use ".." as it has a special meaning
                    var parentBlob = new Blob(ParentFolderName, BlobItemKind.Folder);
                    if (finalItems.Count == 0)
                    {
                        finalItems.Add(parentBlob);
                    }
                    else
                    {
                        finalItems.Insert(0, parentBlob);
                    }
                }

                Blobs.AddAll(finalItems, true);

                RefreshStatusBar();

                FilterBlobs();

                HasError = false;

                BlobsReloaded?.Invoke(Blobs.Count);
            }
            catch (Exception ex)
            {
                HasError     = true;
                ErrorMessage = ex.Message;
                ErrorDetails = ex.StackTrace;
            }
            finally
            {
                IsLoading = false;
            }
        }
示例#7
0
        public async Task <IReadOnlyCollection <Blob> > InternalListAsync(ListOptions options, CancellationToken cancellationToken)
        {
            if (StoragePath.IsRootPath(options.FolderPath))
            {
                //only filesystems are in the root path
                var result = new List <Blob>(await ListFilesystemsAsBlobsAsync(cancellationToken).ConfigureAwait(false));

                if (options.Recurse)
                {
                    foreach (Blob folder in result.Where(b => b.IsFolder).ToList())
                    {
                        int?maxResults = options.MaxResults == null
                     ? null
                     : (int?)(options.MaxResults.Value - result.Count);

                        result.AddRange(await ListPathAsync(folder, maxResults, options, cancellationToken).ConfigureAwait(false));
                    }
                }

                return(result);
            }
            else
            {
                return(await ListPathAsync(options.FolderPath, options.MaxResults, options, cancellationToken).ConfigureAwait(false));
            }
        }
示例#8
0
        public async Task <IReadOnlyCollection <Blob> > ListAsync(ListOptions options, CancellationToken cancellationToken)
        {
            if (options == null)
            {
                options = new ListOptions();
            }

            GenericValidation.CheckBlobPrefix(options.FilePrefix);

            if (!StoragePath.IsRootPath(options.FolderPath))
            {
                return(new List <Blob>());
            }

            var secretNames         = new List <Blob>();
            IPage <SecretItem> page = await _vaultClient.GetSecretsAsync(_vaultUri).ConfigureAwait(false);

            do
            {
                var ids = page
                          .Select((Func <SecretItem, Blob>)AzureKeyVaultBlobStorageProvider.ToBlobId)
                          .Where(options.IsMatch)
                          .Where(s => options.BrowseFilter == null || options.BrowseFilter(s))
                          .ToList();
                secretNames.AddRange(ids);

                if (options.MaxResults != null && secretNames.Count >= options.MaxResults.Value)
                {
                    return(secretNames.Take(options.MaxResults.Value).ToList());
                }
            }while (page.NextPageLink != null && (page = await _vaultClient.GetSecretsNextAsync(page.NextPageLink).ConfigureAwait(false)) != null);

            return(secretNames);
        }
示例#9
0
        /// <summary>
        /// Prepends path to this blob's path without modifying blob's properties
        /// </summary>
        /// <param name="path"></param>
        public void PrependPath(string path)
        {
            if (path == null || StoragePath.IsRootPath(path))
            {
                return;
            }

            FolderPath = StoragePath.Combine(path, FolderPath);
        }
示例#10
0
        public Task <IReadOnlyCollection <Blob> > ListAsync(ListOptions options, CancellationToken cancellationToken = default)
        {
            lock (_lock)
            {
                if (!File.Exists(_filePath))
                {
                    return(Task.FromResult <IReadOnlyCollection <Blob> >(new List <Blob>()));
                }

                ZipArchive archive = GetArchive(false);

                if (options == null)
                {
                    options = new ListOptions();
                }

                IEnumerable <Blob> blobs = archive.Entries.Select(ze => new Blob(ze.FullName, BlobItemKind.File));

                if (options.FilePrefix != null)
                {
                    blobs = blobs.Where(id => id.Name.StartsWith(options.FilePrefix));
                }

                //find ones that belong to this folder
                if (!StoragePath.IsRootPath(options.FolderPath))
                {
                    blobs = blobs.Where(id => id.FullPath.StartsWith(options.FolderPath));
                }

                blobs = AppendVirtualFolders(options.FolderPath ?? StoragePath.RootFolderPath, blobs.ToList());

                //cut off sub-items
                if (!options.Recurse)
                {
                    blobs = blobs
                            .Select(b => new { rp = b.FullPath.Substring(options.FolderPath.Length + 1), b = b })
                            .Where(a => !a.rp.Contains(StoragePath.PathSeparator))
                            .Select(a => a.b);


                    //blobs = blobs.Where(id => !id.FullPath.Substring(0, options.FolderPath.Length).Contains(StoragePath.PathSeparator));
                }

                if (options.BrowseFilter != null)
                {
                    blobs = blobs.Where(id => options.BrowseFilter(id));
                }

                if (options.MaxResults != null)
                {
                    blobs = blobs.Take(options.MaxResults.Value);
                }

                return(Task.FromResult <IReadOnlyCollection <Blob> >(blobs.ToList()));
            }
        }
        private async Task <IReadOnlyCollection <Blob> > LegacyListAtAsync(string path, ListOptions options, CancellationToken cancellationToken)
        {
            PagedAsyncEnumerable <Objects, Object> objects = _client.ListObjectsAsync(
                _bucketName,
                StoragePath.IsRootPath(options.FolderPath) ? null : options.FolderPath,
                new ListObjectsOptions
            {
                Delimiter = options.Recurse ? null : "/"
            });

            return(await GConvert.ToBlobsAsync(objects, options).ConfigureAwait(false));
        }
        public async Task <IReadOnlyCollection <BlobId> > ListAsync(ListOptions options, CancellationToken cancellationToken = default)
        {
            if (options == null)
            {
                options = new ListOptions();
            }

            var result     = new List <BlobId>();
            var containers = new List <CloudBlobContainer>();
            CloudBlobContainer fixedContainer = await GetFixedContainerAsync();

            if (fixedContainer != null)
            {
                containers.Add(fixedContainer);
            }
            else if (StoragePath.IsRootPath(options.FolderPath))
            {
                // list all of the containers
                containers.AddRange(await GetCloudBlobContainersAsync(cancellationToken));

                //represent containers as folders in the result
                result.AddRange(containers.Select(c => new BlobId(c.Name, BlobItemKind.Folder)));

                if (!options.Recurse)
                {
                    return(result);
                }
            }
            else
            {
                (CloudBlobContainer container, string path) = await GetPartsAsync(options.FolderPath, false);

                if (container == null)
                {
                    return(new List <BlobId>());
                }
                options            = options.Clone();
                options.FolderPath = path; //scan from subpath now
                containers.Add(container);

                //add container as search result
                //result.Add(new BlobId(container.Name, BlobItemKind.Folder));
            }

            await Task.WhenAll(containers.Select(c => ListAsync(c, fixedContainer, result, options, cancellationToken)));

            if (options.MaxResults != null)
            {
                result = result.Take(options.MaxResults.Value).ToList();
            }

            return(result);
        }
 private static string?FormatFolderPrefix(string folderPath)
 {
     folderPath = StoragePath.Normalize(folderPath);
     if (StoragePath.IsRootPath(folderPath))
     {
         return(null);
     }
     if (!folderPath.EndsWith("/"))
     {
         folderPath += "/";
     }
     return(folderPath.TrimStart('/'));
 }
示例#14
0
        private void CreateCommands()
        {
            GoLevelUpCommand = new RelayCommand(() =>
            {
                GoLevelUp();
            },
                                                () => !StoragePath.IsRootPath(FolderPath));

            ExternalOpenCommand = new RelayCommand(() =>
            {
                ExternalOpenAsync(SelectedBlob).Forget();
            },
                                                   () => HasOneFileSelected);
        }
示例#15
0
        private void AddVirtualFolderHierarchy(Blob fileBlob)
        {
            string path = fileBlob.FolderPath;

            while (!StoragePath.IsRootPath(path))
            {
                var vf = new Blob(path, BlobItemKind.Folder);
                _pathToTag[path] = new Tag {
                    blob = vf
                };

                path = StoragePath.GetParent(path);
            }
        }
示例#16
0
        private static void AssumeImplicitPrefixes(string absoluteRoot, List <Blob> blobs)
        {
            absoluteRoot = StoragePath.Normalize(absoluteRoot);

            List <Blob> implicitFolders = blobs
                                          .Select(b => b.FullPath)
                                          .Select(p => p.Substring(absoluteRoot.Length))
                                          .Select(p => StoragePath.GetParent(p))
                                          .Where(p => !StoragePath.IsRootPath(p))
                                          .Distinct()
                                          .Select(p => new Blob(p, BlobItemKind.Folder))
                                          .ToList();

            blobs.AddRange(implicitFolders);
        }
示例#17
0
        private async Task DiscoverBlobsWorkflowAsync(IReadOnlyCollection <Blob> sourceBlobs)
        {
            foreach (Blob blob in sourceBlobs)
            {
                if (_sourceRoot == null)
                {
                    _sourceRoot = StoragePath.Normalize(blob.FolderPath);
                    if (StoragePath.IsRootPath(_sourceRoot))
                    {
                        _sourceRoot = string.Empty;
                    }
                }

                await DiscoverBlobAsync(blob);
            }
        }
示例#18
0
        /// <summary>
        /// Changes full path of this blob without modifying any other property
        /// </summary>
        public void SetFullPath(string fullPath)
        {
            string path = StoragePath.Normalize(fullPath);

            if (StoragePath.IsRootPath(path))
            {
                Name       = StoragePath.RootFolderPath;
                FolderPath = StoragePath.RootFolderPath;
            }
            else
            {
                string[] parts = StoragePath.Split(path);

                Name       = parts.Last();
                FolderPath = StoragePath.GetParent(path);
            }
        }
示例#19
0
        public override async Task WriteAsync(
            string fullPath, Stream dataStream, bool append = false, CancellationToken cancellationToken = default)
        {
            string path = StoragePath.Normalize(fullPath);

            //import will fail unless all the parent folders exist, so make sure they do
            string parent = StoragePath.GetParent(path);

            if (!StoragePath.IsRootPath(parent))
            {
                await _api.Mkdirs(StoragePath.Normalize(parent)).ConfigureAwait(false);
            }

            GetImportParameters(path, out ExportFormat exportFormat, out Language? language);

            await _api.Import(path, exportFormat, language, dataStream.ToByteArray(), true).ConfigureAwait(false);
        }
        public async Task <IReadOnlyCollection <Blob> > ListAsync(ListOptions options, CancellationToken cancellationToken = default)
        {
            if (options == null)
            {
                options = new ListOptions();
            }

            var result     = new List <Blob>();
            var containers = new List <CloudBlobContainer>();

            if (StoragePath.IsRootPath(options.FolderPath))
            {
                // list all of the containers
                containers.AddRange(await GetCloudBlobContainersAsync(cancellationToken));

                //represent containers as folders in the result
                result.AddRange(containers.Select(AzConvert.ToBlob));

                if (!options.Recurse)
                {
                    return(result);
                }
            }
            else
            {
                (CloudBlobContainer container, string path) = await GetPartsAsync(options.FolderPath, false).ConfigureAwait(false);

                if (container == null)
                {
                    return(new List <Blob>());
                }
                options            = options.Clone();
                options.FolderPath = path; //scan from subpath now
                containers.Add(container);
            }

            await Task.WhenAll(containers.Select(c => ListAsync(c, result, options, cancellationToken))).ConfigureAwait(false);

            if (options.MaxResults != null)
            {
                result = result.Take(options.MaxResults.Value).ToList();
            }

            return(result);
        }
示例#21
0
        private async Task DeleteAsync(string fullPath, CancellationToken cancellationToken)
        {
            DecomposePath(fullPath, out string fs, out string rp, false);

            if (StoragePath.IsRootPath(rp))
            {
                await _restApi.DeleteFilesystemAsync(fs).ConfigureAwait(false);
            }
            else
            {
                try
                {
                    await _restApi.DeletePathAsync(fs, rp, true).ConfigureAwait(false);
                }
                catch (ApiException ex) when(ex.StatusCode == HttpStatusCode.NotFound)
                {
                    // file not found, ignore
                }
            }
        }
示例#22
0
        protected override async Task <IReadOnlyCollection <Blob> > ListAtAsync(
            string path, ListOptions options, CancellationToken cancellationToken)
        {
            if (StoragePath.IsRootPath(path))
            {
                //list file shares

                ShareResultSegment shares = await _client.ListSharesSegmentedAsync(null, cancellationToken).ConfigureAwait(false);

                return(shares.Results.Select(AzConvert.ToBlob).ToList());
            }
            else
            {
                var chunk = new List <Blob>();

                CloudFileDirectory dir = await GetDirectoryReferenceAsync(path, cancellationToken).ConfigureAwait(false);

                FileContinuationToken token = null;
                do
                {
                    try
                    {
                        FileResultSegment segment = await dir.ListFilesAndDirectoriesSegmentedAsync(options.FilePrefix, token, cancellationToken).ConfigureAwait(false);

                        token = segment.ContinuationToken;

                        chunk.AddRange(segment.Results.Select(r => AzConvert.ToBlob(path, r)));
                    }
                    catch (AzStorageException ex) when(ex.RequestInformation.ErrorCode == "ShareNotFound")
                    {
                        break;
                    }
                    catch (AzStorageException ex) when(ex.RequestInformation.ErrorCode == "ResourceNotFound")
                    {
                        break;
                    }
                }while(token != null);

                return(chunk);
            }
        }
示例#23
0
        public Task <IReadOnlyCollection <Blob> > ListAsync(ListOptions options, CancellationToken cancellationToken)
        {
            if (options == null)
            {
                options = new ListOptions();
            }

            IEnumerable <KeyValuePair <string, Tag> > query = _pathToTag;

            //limit by folder path
            if (options.Recurse)
            {
                if (!StoragePath.IsRootPath(options.FolderPath))
                {
                    string prefix = options.FolderPath + StoragePath.PathSeparatorString;

                    query = query.Where(p => p.Key.StartsWith(prefix));
                }
            }
            else
            {
                query = query.Where(p => StoragePath.ComparePath(p.Value.blob.FolderPath, options.FolderPath));
            }

            //prefix
            query = query.Where(p => options.IsMatch(p.Value.blob));

            //browser filter
            query = query.Where(p => options.BrowseFilter == null || options.BrowseFilter(p.Value.blob));

            //limit
            if (options.MaxResults != null)
            {
                query = query.Take(options.MaxResults.Value);
            }

            IReadOnlyCollection <Blob> matches = query.Select(p => p.Value.blob).ToList();

            return(Task.FromResult(matches));
        }
示例#24
0
        protected override async Task <IReadOnlyCollection <Blob> > ListAtAsync(
            string path, ListOptions options, CancellationToken cancellationToken)
        {
            var r = new List <Blob>();

            // listing from "/secrets"
            if (StoragePath.IsRootPath(path))
            {
                IEnumerable <SecretScope> scopes = await _api.ListScopes().ConfigureAwait(false);

                foreach (SecretScope scope in scopes)
                {
                    var scopeBlob = new Blob(scope.Name, BlobItemKind.Folder);
                    scopeBlob.TryAddProperties(
                        "Backend", scope.BackendType,
                        "ObjectType", "secretScope");

                    IEnumerable <AclItem> acl = await _api.ListSecretAcl(scope.Name).ConfigureAwait(false);

                    scopeBlob.Properties.Add("ACL", string.Join(";", acl.Select(a => $"{a.Principal}:{a.Permission}")));
                    r.Add(scopeBlob);
                }
                return(r);
            }

            // listing from "/secrets/[scope name]

            string scopeName   = StoragePath.Split(path)[0];
            var    secretNames = (await _api.ListSecrets(scopeName).ConfigureAwait(false)).ToList();

            foreach (SecretMetadata sm in secretNames)
            {
                var sb = new Blob(scopeName, sm.Key, BlobItemKind.File);
                sb.LastModificationTime = sm.LastUpdatedTimestamp;
                sb.TryAddProperties(
                    "ObjectType", "secret");
                r.Add(sb);
            }
            return(r);
        }
        public async Task <IReadOnlyCollection <Blob> > ListAsync(ListOptions options, CancellationToken cancellationToken)
        {
            if (options == null)
            {
                options = new ListOptions();
            }

            GenericValidation.CheckBlobPrefix(options.FilePrefix);

            if (!StoragePath.IsRootPath(options.FolderPath))
            {
                return(new List <Blob>());
            }

            var secrets = new List <Blob>();

            await foreach (SecretProperties secretProperties in _client.GetPropertiesOfSecretsAsync(cancellationToken).ConfigureAwait(false))
            {
                Blob blob = ToBlob(secretProperties);
                if (!options.IsMatch(blob))
                {
                    continue;
                }

                if (options.BrowseFilter != null && !options.BrowseFilter(blob))
                {
                    continue;
                }

                secrets.Add(blob);

                if (options.MaxResults != null && secrets.Count >= options.MaxResults.Value)
                {
                    break;
                }
            }

            return(secrets);
        }
示例#26
0
        public async Task DeleteAsync(string fullPath, CancellationToken cancellationToken)
        {
            DecomposePath(fullPath, out string fs, out string rp, false);

            if (StoragePath.IsRootPath(rp))
            {
                await DeleteFilesystemAsync(fs, cancellationToken).ConfigureAwait(false);
            }
            else
            {
                try
                {
                    await InvokeAsync <Void>(
                        $"{fs}/{rp}?recursive=true",
                        RequestMethod.Delete,
                        cancellationToken).ConfigureAwait(false);
                }
                catch (RequestFailedException ex) when(ex.ErrorCode == "PathNotFound")
                {
                    // file not found, ignore
                }
            }
        }
示例#27
0
        public static IReadOnlyCollection <Blob> ToBlobs(this ListObjectsV2Response response, ListOptions options)
        {
            var result = new List <Blob>();

            //the files are listed as the S3Objects member, but they don't specifically contain folders,
            //but even if they do, they need to be filtered out

            result.AddRange(
                response.S3Objects
                .Where(b => !b.Key.EndsWith("/")) //check if this is "virtual folder" as S3 console creates them (rubbish)
                .Select(b => b.ToBlob())
                .Where(options.IsMatch)
                .Where(b => options.BrowseFilter == null || options.BrowseFilter(b)));

            //subfolders are listed in another field (what a funny name!)

            //prefix is absolute too
            result.AddRange(
                response.CommonPrefixes
                .Where(p => !StoragePath.IsRootPath(p))
                .Select(p => new Blob(p, BlobItemKind.Folder)));

            return(result);
        }
示例#28
0
        public async Task <Blob> GetBlobAsync(string fullPath, CancellationToken cancellationToken)
        {
            DecomposePath(fullPath, out string fs, out string rp, false);

            if (StoragePath.IsRootPath(rp))
            {
                try
                {
                    (Void _, IDictionary <string, string> headers) = await InvokeExtraAsync <Void>(
                        $"{fs}?resource=filesystem",
                        RequestMethod.Head,
                        cancellationToken).ConfigureAwait(false);

                    return(AzConvert.ToBlob(fullPath, headers, true));
                }
                catch (RequestFailedException ex) when(ex.ErrorCode == "FilesystemNotFound")
                {
                    //filesystem doesn't exist
                    return(null);
                }
            }

            try
            {
                (Void _, IDictionary <string, string> fheaders) = await InvokeExtraAsync <Void>(
                    $"{fs}/{rp.UrlEncode()}?action=getProperties",
                    RequestMethod.Head,
                    cancellationToken).ConfigureAwait(false);

                return(AzConvert.ToBlob(fullPath, fheaders, false));
            }
            catch (RequestFailedException ex) when(ex.ErrorCode == "PathNotFound")
            {
                return(null);
            }
        }