<commit_before>// Copyright 2015 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

// +build windows

package libdokan

import (
	"sync"

	keybase1 "github.com/keybase/client/go/protocol"
	"github.com/keybase/kbfs/dokan"
	"github.com/keybase/kbfs/libkbfs"
	"golang.org/x/net/context"
)

// FolderList is a node that can list all of the logged-in user's
// favorite top-level folders, on either a public or private basis.
type FolderList struct {
	emptyFile
	fs *FS
	// only accept public folders
	public bool

	mu      sync.Mutex
	folders map[string]*Dir
}

// GetFileInformation for dokan.
func (*FolderList) GetFileInformation(*dokan.FileInfo) (*dokan.Stat, error) {
	return defaultDirectoryInformation()
}

// open tries to open the correct thing. Following aliases and deferring to
// Dir.open as necessary.
func (fl *FolderList) open(fi *dokan.FileInfo, path []string, caf *createData) (f dokan.File, isDir bool, err error) {
	ctx := context.TODO()
	ctx = NewContextWithOpID(ctx, fl.fs.log)
	fl.fs.log.CDebugf(ctx, "FL Lookup %s", path)
	defer func() { fl.fs.reportErr(ctx, err) }()

	if len(path) == 0 {
		if caf.mayNotBeDirectory() {
			return nil, true, dokan.ErrFileIsADirectory
		}
		return fl, true, nil
	}

	for maxRedirections := 30; maxRedirections > 0; maxRedirections-- {
		name := path[0]

		fl.mu.Lock()
		child, ok := fl.folders[name]
		fl.mu.Unlock()

		if ok {
			return child.open(fi, path[1:], caf)
		}

		dh, err := libkbfs.ParseTlfHandle(ctx, fl.fs.config, name)
		if err != nil {
			return nil, false, err
		}

		if fl.public && !dh.HasPublic() {
			// no public folder exists for this folder
			return nil, false, dokan.ErrObjectPathNotFound
		}

		if canon := dh.ToString(ctx, fl.fs.config); canon != name {
			if len(path) == 1 && caf.isOpenReparsePoint() {
				return &Alias{canon: canon}, false, nil
			}
			path[0] = canon
			continue
		}

		if fl.public {
			dh = &libkbfs.TlfHandle{
				Writers: dh.Writers,
				Readers: []keybase1.UID{keybase1.PublicUID},
			}
		}

		rootNode, _, err := fl.fs.config.KBFSOps().GetOrCreateRootNodeForHandle(ctx, dh, libkbfs.MasterBranch)
		if err != nil {
			return nil, false, err
		}

		folderBranch := rootNode.GetFolderBranch()
		folder := &Folder{
			fs:           fl.fs,
			list:         fl,
			name:         name,
			folderBranch: folderBranch,
			nodes:        map[libkbfs.NodeID]dokan.File{},
		}

		// TODO unregister all at unmount
		if err := fl.fs.config.Notifier().RegisterForChanges([]libkbfs.FolderBranch{folderBranch}, folder); err != nil {
			return nil, false, err
		}

		fl.mu.Lock()
		child = newDir(folder, rootNode, path[0], nil)
		folder.nodes[rootNode.GetID()] = child
		fl.folders[name] = child
		fl.mu.Unlock()
		return child.open(fi, path[1:], caf)
	}
	return nil, false, dokan.ErrObjectPathNotFound
}

func (fl *FolderList) forgetFolder(f *Folder) {
	fl.mu.Lock()
	defer fl.mu.Unlock()

	if err := fl.fs.config.Notifier().UnregisterFromChanges([]libkbfs.FolderBranch{f.folderBranch}, f); err != nil {
		fl.fs.log.Info("cannot unregister change notifier for folder %q: %v",
			f.name, err)
	}
	delete(fl.folders, f.name)
}

func (fl *FolderList) getDirent(ctx context.Context, work <-chan *libkbfs.Favorite, results chan<- dokan.NamedStat) error {
	for {
		select {
		case fav, ok := <-work:
			if !ok {
				return nil
			}
			var ns dokan.NamedStat
			ns.Name = fav.Name
			ns.FileAttributes = fileAttributeDirectory
			ns.NumberOfLinks = 1
			results <- ns
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

// FindFiles for dokan.
func (fl *FolderList) FindFiles(fi *dokan.FileInfo, callback func(*dokan.NamedStat) error) (err error) {<commit_msg>libdokan: Display symlinks in directory listing for FolderList<commit_after>// Copyright 2015 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

// +build windows

package libdokan

import (
	"sync"

	keybase1 "github.com/keybase/client/go/protocol"
	"github.com/keybase/kbfs/dokan"
	"github.com/keybase/kbfs/libkbfs"
	"golang.org/x/net/context"
)

// FolderList is a node that can list all of the logged-in user's
// favorite top-level folders, on either a public or private basis.
type FolderList struct {
	emptyFile
	fs *FS
	// only accept public folders
	public bool

	mu      sync.Mutex
	folders map[string]*Dir
}

// GetFileInformation for dokan.
func (*FolderList) GetFileInformation(*dokan.FileInfo) (*dokan.Stat, error) {
	return defaultDirectoryInformation()
}

// open tries to open the correct thing. Following aliases and deferring to
// Dir.open as necessary.
func (fl *FolderList) open(fi *dokan.FileInfo, path []string, caf *createData) (f dokan.File, isDir bool, err error) {
	ctx := context.TODO()
	ctx = NewContextWithOpID(ctx, fl.fs.log)
	fl.fs.log.CDebugf(ctx, "FL Lookup %s", path)
	defer func() { fl.fs.reportErr(ctx, err) }()

	if len(path) == 0 {
		if caf.mayNotBeDirectory() {
			return nil, true, dokan.ErrFileIsADirectory
		}
		return fl, true, nil
	}

	for maxRedirections := 30; maxRedirections > 0; maxRedirections-- {
		name := path[0]

		fl.mu.Lock()
		child, ok := fl.folders[name]
		fl.mu.Unlock()

		if ok {
			return child.open(fi, path[1:], caf)
		}

		dh, err := libkbfs.ParseTlfHandle(ctx, fl.fs.config, name)
		if err != nil {
			return nil, false, err
		}

		if fl.public && !dh.HasPublic() {
			// no public folder exists for this folder
			return nil, false, dokan.ErrObjectPathNotFound
		}

		if canon := dh.ToString(ctx, fl.fs.config); canon != name {
			if len(path) == 1 && caf.isOpenReparsePoint() {
				return &Alias{canon: canon}, false, nil
			}
			path[0] = canon
			continue
		}

		if fl.public {
			dh = &libkbfs.TlfHandle{
				Writers: dh.Writers,
				Readers: []keybase1.UID{keybase1.PublicUID},
			}
		}

		rootNode, _, err := fl.fs.config.KBFSOps().GetOrCreateRootNodeForHandle(ctx, dh, libkbfs.MasterBranch)
		if err != nil {
			return nil, false, err
		}

		folderBranch := rootNode.GetFolderBranch()
		folder := &Folder{
			fs:           fl.fs,
			list:         fl,
			name:         name,
			folderBranch: folderBranch,
			nodes:        map[libkbfs.NodeID]dokan.File{},
		}

		// TODO unregister all at unmount
		if err := fl.fs.config.Notifier().RegisterForChanges([]libkbfs.FolderBranch{folderBranch}, folder); err != nil {
			return nil, false, err
		}

		fl.mu.Lock()
		child = newDir(folder, rootNode, path[0], nil)
		folder.nodes[rootNode.GetID()] = child
		fl.folders[name] = child
		fl.mu.Unlock()
		return child.open(fi, path[1:], caf)
	}
	return nil, false, dokan.ErrObjectPathNotFound
}

func (fl *FolderList) forgetFolder(f *Folder) {
	fl.mu.Lock()
	defer fl.mu.Unlock()

	if err := fl.fs.config.Notifier().UnregisterFromChanges([]libkbfs.FolderBranch{f.folderBranch}, f); err != nil {
		fl.fs.log.Info("cannot unregister change notifier for folder %q: %v",
			f.name, err)
	}
	delete(fl.folders, f.name)
}

func (fl *FolderList) getDirent(ctx context.Context, work <-chan *libkbfs.Favorite, results chan<- dokan.NamedStat) error {
	for {
		select {
		case fav, ok := <-work:
			if !ok {
				return nil
			}

			dh, err := libkbfs.ParseTlfHandle(ctx, fl.fs.config, fav.Name)
			if err != nil {
				break
			}

			if fl.public && !dh.HasPublic() {
				break
			}

			var ns dokan.NamedStat
			ns.Name = fav.Name
			ns.FileAttributes = fileAttributeDirectory
			ns.NumberOfLinks = 1

			if canon := dh.ToString(ctx, fl.fs.config); canon != fav.Name {
				ns.FileAttributes = fileAttributeReparsePoint
				ns.ReparsePointTag = reparsePointTagSymlink
			}

			results <- ns
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

// FindFiles for dokan.
func (fl *FolderList) FindFiles(fi *dokan.FileInfo, callback func(*dokan.NamedStat) error) (err error) {
{'fraction_non_alphanumeric': 0.1351799846821547, 'fraction_numerical': 0.004595353586928772, 'mean_word_length': 3.3479467258601554, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6474312', 'n_tokens_mistral': 3126, 'n_tokens_neox': 2811, 'n_words': 730}