Golang SFTP directory watcher failed to execute Lstat

Viewed 392

I have an extremely rare error, when implementing a watcher for a remote directory (SFTP) it gives me a syscall error, when trying to execute the Lstat command.

The error is as follows:

golang.org/x/sys/unix.ENOENT (2)
lstat /home/user/directory: no such file or directory

This is the line that gives the error: _, _, e1 := syscall(funcPC(libc_lstat64_trampoline), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)

The directory path to watch is /home/user/directory, and the implementation is as follows:

SFTP Connection

func StartSFTPConnection() *sftp.Client {
    // SFTP connection data
    host := os.Getenv("FTP_HOST")
    port := os.Getenv("FTP_PORT")
    user := os.Getenv("FTP_USER")
    pass := os.Getenv("FTP_PASSWORD")

    hostKey := getHostKey(host)

    if _, err := fmt.Fprintf(os.Stdout, "Connecting to %s ...\n", host); err != nil {
        log.Fatal(err)
    }

    var auths []ssh.AuthMethod

    // Try to use $SSH_AUTH_SOCK which contains the path of the unix file socket that the sshd agent uses
    // for communication with other processes.
    if aconn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
        auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(aconn).Signers))
    }

    // Use password authentication if provided
    if pass != "" {
        auths = append(auths, ssh.Password(pass))
    }

    // Initialize client configuration
    config := ssh.ClientConfig{
        User: user,
        Auth: auths,
        HostKeyCallback: ssh.FixedHostKey(hostKey),
    }

    addr := fmt.Sprintf("%s:%s", host, port)

    // Connect to server
    conn, err := ssh.Dial("tcp", addr, &config)
    if err != nil {
        if _, err := fmt.Fprintf(os.Stderr, "Failed to connecto to [%s]: %v\n", addr, err); err != nil {
            log.Fatal(err)
        }
    }

    // Create new SFTP client
    sc, err := sftp.NewClient(conn)
    if err != nil {
        if _, err := fmt.Fprintf(os.Stderr, "Unable to start SFTP subsystem: %v\n", err); err != nil {
            log.Fatal(err)
        }
    }

    return sc
}

Watcher

// FileWatcher checks for new files in a folder
func FileWatcher() (err error) {
    var sc = sftp2.StartSFTPConnection()

    watcher, _ = fsnotify.NewWatcher()
    defer func(watcher *fsnotify.Watcher) {
        if err := watcher.Close(); err != nil {
            log.Fatal(err)
        }
    }(watcher)

    wk := sc.Walk(os.Getenv("WATCH_DIR"))

    for wk.Step() {
        if wk.Err() != nil {
            continue
        }

        if err := watchDir(wk.Path(), wk.Stat()); err != nil {
            fmt.Println("There are some errors in FTP Watcher.")
        }
    }

    done := make(chan bool)

    // watch for changes in folder
    go func() {
        for {
            select {
            // watch for events
            case event := <-watcher.Events:
                fmt.Printf("New file added in `WATCH` path. Event %#v.\n", event.Op)

                switch {

                case event.Op&fsnotify.Write == fsnotify.Write:
                    fmt.Printf("Write:  %s: %s", event.Op, event.Name)
                case event.Op&fsnotify.Create == fsnotify.Create:
                    fmt.Printf("Create: %s: %s", event.Op, event.Name)
                    parser.FileParser(event.Name)
                case event.Op&fsnotify.Remove == fsnotify.Remove:
                    fmt.Printf("Remove: %s: %s", event.Op, event.Name)
                case event.Op&fsnotify.Rename == fsnotify.Rename:
                    fmt.Printf("Rename: %s: %s", event.Op, event.Name)
                case event.Op&fsnotify.Chmod == fsnotify.Chmod:
                    fmt.Printf("Chmod:  %s: %s", event.Op, event.Name)

                }

            // watch for errors
            case err := <-watcher.Errors:
                fmt.Println(err)
            }
        }
    }()

    <-done

    // return
    return nil
}

// watchDir gets run as a walk func, searching for directories to add watchers to
func watchDir(path string, fi os.FileInfo) error {

    // since fsnotify can watch all the files in a directory, watchers only need to be added to each nested directory
    if fi.Mode().IsDir() {
        return watcher.Add(path)
    }

    return nil
}

What could be wrong? Also, the directory is a normal dir, not a symlink.

1 Answers

I guess fsnotify can't be used with SFTP protocol. In your watchDir function you add /home/user/directory directory that supposed to be in your computer's FS, not on dedicated server. I tried with my local directories and didn't have any errors, but still didn't find another solution to watch SFTP directory.

Related