github.com/artpar/rclone@v1.67.3/backend/ftp/ftp.go (about)

     1  // Package ftp interfaces with FTP servers
     2  package ftp
     3  
     4  import (
     5  	"context"
     6  	"crypto/tls"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"net"
    11  	"net/textproto"
    12  	"path"
    13  	"runtime"
    14  	"strings"
    15  	"sync"
    16  	"time"
    17  
    18  	"github.com/artpar/rclone/fs"
    19  	"github.com/artpar/rclone/fs/accounting"
    20  	"github.com/artpar/rclone/fs/config"
    21  	"github.com/artpar/rclone/fs/config/configmap"
    22  	"github.com/artpar/rclone/fs/config/configstruct"
    23  	"github.com/artpar/rclone/fs/config/obscure"
    24  	"github.com/artpar/rclone/fs/fserrors"
    25  	"github.com/artpar/rclone/fs/fshttp"
    26  	"github.com/artpar/rclone/fs/hash"
    27  	"github.com/artpar/rclone/lib/encoder"
    28  	"github.com/artpar/rclone/lib/env"
    29  	"github.com/artpar/rclone/lib/pacer"
    30  	"github.com/artpar/rclone/lib/proxy"
    31  	"github.com/artpar/rclone/lib/readers"
    32  	"github.com/jlaffaye/ftp"
    33  )
    34  
    35  var (
    36  	currentUser = env.CurrentUser()
    37  )
    38  
    39  const (
    40  	minSleep      = 10 * time.Millisecond
    41  	maxSleep      = 2 * time.Second
    42  	decayConstant = 2 // bigger for slower decay, exponential
    43  )
    44  
    45  // Register with Fs
    46  func init() {
    47  	fs.Register(&fs.RegInfo{
    48  		Name:        "ftp",
    49  		Description: "FTP",
    50  		NewFs:       NewFs,
    51  		Options: []fs.Option{{
    52  			Name:      "host",
    53  			Help:      "FTP host to connect to.\n\nE.g. \"ftp.example.com\".",
    54  			Required:  true,
    55  			Sensitive: true,
    56  		}, {
    57  			Name:      "user",
    58  			Help:      "FTP username.",
    59  			Default:   currentUser,
    60  			Sensitive: true,
    61  		}, {
    62  			Name:    "port",
    63  			Help:    "FTP port number.",
    64  			Default: 21,
    65  		}, {
    66  			Name:       "pass",
    67  			Help:       "FTP password.",
    68  			IsPassword: true,
    69  		}, {
    70  			Name: "tls",
    71  			Help: `Use Implicit FTPS (FTP over TLS).
    72  
    73  When using implicit FTP over TLS the client connects using TLS
    74  right from the start which breaks compatibility with
    75  non-TLS-aware servers. This is usually served over port 990 rather
    76  than port 21. Cannot be used in combination with explicit FTPS.`,
    77  			Default: false,
    78  		}, {
    79  			Name: "explicit_tls",
    80  			Help: `Use Explicit FTPS (FTP over TLS).
    81  
    82  When using explicit FTP over TLS the client explicitly requests
    83  security from the server in order to upgrade a plain text connection
    84  to an encrypted one. Cannot be used in combination with implicit FTPS.`,
    85  			Default: false,
    86  		}, {
    87  			Name: "concurrency",
    88  			Help: strings.Replace(`Maximum number of FTP simultaneous connections, 0 for unlimited.
    89  
    90  Note that setting this is very likely to cause deadlocks so it should
    91  be used with care.
    92  
    93  If you are doing a sync or copy then make sure concurrency is one more
    94  than the sum of |--transfers| and |--checkers|.
    95  
    96  If you use |--check-first| then it just needs to be one more than the
    97  maximum of |--checkers| and |--transfers|.
    98  
    99  So for |concurrency 3| you'd use |--checkers 2 --transfers 2
   100  --check-first| or |--checkers 1 --transfers 1|.
   101  
   102  `, "|", "`", -1),
   103  			Default:  0,
   104  			Advanced: true,
   105  		}, {
   106  			Name:     "no_check_certificate",
   107  			Help:     "Do not verify the TLS certificate of the server.",
   108  			Default:  false,
   109  			Advanced: true,
   110  		}, {
   111  			Name:     "disable_epsv",
   112  			Help:     "Disable using EPSV even if server advertises support.",
   113  			Default:  false,
   114  			Advanced: true,
   115  		}, {
   116  			Name:     "disable_mlsd",
   117  			Help:     "Disable using MLSD even if server advertises support.",
   118  			Default:  false,
   119  			Advanced: true,
   120  		}, {
   121  			Name:     "disable_utf8",
   122  			Help:     "Disable using UTF-8 even if server advertises support.",
   123  			Default:  false,
   124  			Advanced: true,
   125  		}, {
   126  			Name:     "writing_mdtm",
   127  			Help:     "Use MDTM to set modification time (VsFtpd quirk)",
   128  			Default:  false,
   129  			Advanced: true,
   130  		}, {
   131  			Name:     "force_list_hidden",
   132  			Help:     "Use LIST -a to force listing of hidden files and folders. This will disable the use of MLSD.",
   133  			Default:  false,
   134  			Advanced: true,
   135  		}, {
   136  			Name:    "idle_timeout",
   137  			Default: fs.Duration(60 * time.Second),
   138  			Help: `Max time before closing idle connections.
   139  
   140  If no connections have been returned to the connection pool in the time
   141  given, rclone will empty the connection pool.
   142  
   143  Set to 0 to keep connections indefinitely.
   144  `,
   145  			Advanced: true,
   146  		}, {
   147  			Name:     "close_timeout",
   148  			Help:     "Maximum time to wait for a response to close.",
   149  			Default:  fs.Duration(60 * time.Second),
   150  			Advanced: true,
   151  		}, {
   152  			Name: "tls_cache_size",
   153  			Help: `Size of TLS session cache for all control and data connections.
   154  
   155  TLS cache allows to resume TLS sessions and reuse PSK between connections.
   156  Increase if default size is not enough resulting in TLS resumption errors.
   157  Enabled by default. Use 0 to disable.`,
   158  			Default:  32,
   159  			Advanced: true,
   160  		}, {
   161  			Name:     "disable_tls13",
   162  			Help:     "Disable TLS 1.3 (workaround for FTP servers with buggy TLS)",
   163  			Default:  false,
   164  			Advanced: true,
   165  		}, {
   166  			Name:     "shut_timeout",
   167  			Help:     "Maximum time to wait for data connection closing status.",
   168  			Default:  fs.Duration(60 * time.Second),
   169  			Advanced: true,
   170  		}, {
   171  			Name:    "ask_password",
   172  			Default: false,
   173  			Help: `Allow asking for FTP password when needed.
   174  
   175  If this is set and no password is supplied then rclone will ask for a password
   176  `,
   177  			Advanced: true,
   178  		}, {
   179  			Name:    "socks_proxy",
   180  			Default: "",
   181  			Help: `Socks 5 proxy host.
   182  		
   183  		Supports the format user:pass@host:port, user@host:port, host:port.
   184  		
   185  		Example:
   186  		
   187  			myUser:myPass@localhost:9005
   188  		`,
   189  			Advanced: true,
   190  		}, {
   191  			Name:     config.ConfigEncoding,
   192  			Help:     config.ConfigEncodingHelp,
   193  			Advanced: true,
   194  			// The FTP protocol can't handle trailing spaces
   195  			// (for instance, pureftpd turns them into '_')
   196  			Default: (encoder.Display |
   197  				encoder.EncodeRightSpace),
   198  			Examples: []fs.OptionExample{{
   199  				Value: "Asterisk,Ctl,Dot,Slash",
   200  				Help:  "ProFTPd can't handle '*' in file names",
   201  			}, {
   202  				Value: "BackSlash,Ctl,Del,Dot,RightSpace,Slash,SquareBracket",
   203  				Help:  "PureFTPd can't handle '[]' or '*' in file names",
   204  			}, {
   205  				Value: "Ctl,LeftPeriod,Slash",
   206  				Help:  "VsFTPd can't handle file names starting with dot",
   207  			}},
   208  		}},
   209  	})
   210  }
   211  
   212  // Options defines the configuration for this backend
   213  type Options struct {
   214  	Host              string               `config:"host"`
   215  	User              string               `config:"user"`
   216  	Pass              string               `config:"pass"`
   217  	Port              string               `config:"port"`
   218  	TLS               bool                 `config:"tls"`
   219  	ExplicitTLS       bool                 `config:"explicit_tls"`
   220  	TLSCacheSize      int                  `config:"tls_cache_size"`
   221  	DisableTLS13      bool                 `config:"disable_tls13"`
   222  	Concurrency       int                  `config:"concurrency"`
   223  	SkipVerifyTLSCert bool                 `config:"no_check_certificate"`
   224  	DisableEPSV       bool                 `config:"disable_epsv"`
   225  	DisableMLSD       bool                 `config:"disable_mlsd"`
   226  	DisableUTF8       bool                 `config:"disable_utf8"`
   227  	WritingMDTM       bool                 `config:"writing_mdtm"`
   228  	ForceListHidden   bool                 `config:"force_list_hidden"`
   229  	IdleTimeout       fs.Duration          `config:"idle_timeout"`
   230  	CloseTimeout      fs.Duration          `config:"close_timeout"`
   231  	ShutTimeout       fs.Duration          `config:"shut_timeout"`
   232  	AskPassword       bool                 `config:"ask_password"`
   233  	Enc               encoder.MultiEncoder `config:"encoding"`
   234  	SocksProxy        string               `config:"socks_proxy"`
   235  }
   236  
   237  // Fs represents a remote FTP server
   238  type Fs struct {
   239  	name     string         // name of this remote
   240  	root     string         // the path we are working on if any
   241  	opt      Options        // parsed options
   242  	ci       *fs.ConfigInfo // global config
   243  	features *fs.Features   // optional features
   244  	url      string
   245  	user     string
   246  	pass     string
   247  	dialAddr string
   248  	poolMu   sync.Mutex
   249  	pool     []*ftp.ServerConn
   250  	drain    *time.Timer // used to drain the pool when we stop using the connections
   251  	tokens   *pacer.TokenDispenser
   252  	pacer    *fs.Pacer // pacer for FTP connections
   253  	fGetTime bool      // true if the ftp library accepts GetTime
   254  	fSetTime bool      // true if the ftp library accepts SetTime
   255  	fLstTime bool      // true if the List call returns precise time
   256  }
   257  
   258  // Object describes an FTP file
   259  type Object struct {
   260  	fs     *Fs
   261  	remote string
   262  	info   *FileInfo
   263  }
   264  
   265  // FileInfo is the metadata known about an FTP file
   266  type FileInfo struct {
   267  	Name    string
   268  	Size    uint64
   269  	ModTime time.Time
   270  	precise bool // true if the time is precise
   271  	IsDir   bool
   272  }
   273  
   274  // ------------------------------------------------------------
   275  
   276  // Name of this fs
   277  func (f *Fs) Name() string {
   278  	return f.name
   279  }
   280  
   281  // Root of the remote (as passed into NewFs)
   282  func (f *Fs) Root() string {
   283  	return f.root
   284  }
   285  
   286  // String returns a description of the FS
   287  func (f *Fs) String() string {
   288  	return f.url
   289  }
   290  
   291  // Features returns the optional features of this Fs
   292  func (f *Fs) Features() *fs.Features {
   293  	return f.features
   294  }
   295  
   296  // Enable debugging output
   297  type debugLog struct {
   298  	mu   sync.Mutex
   299  	auth bool
   300  }
   301  
   302  // Write writes len(p) bytes from p to the underlying data stream. It returns
   303  // the number of bytes written from p (0 <= n <= len(p)) and any error
   304  // encountered that caused the write to stop early. Write must return a non-nil
   305  // error if it returns n < len(p). Write must not modify the slice data, even
   306  // temporarily.
   307  //
   308  // Implementations must not retain p.
   309  //
   310  // This writes debug info to the log
   311  func (dl *debugLog) Write(p []byte) (n int, err error) {
   312  	dl.mu.Lock()
   313  	defer dl.mu.Unlock()
   314  	_, file, _, ok := runtime.Caller(1)
   315  	direction := "FTP Rx"
   316  	if ok && strings.Contains(file, "multi") {
   317  		direction = "FTP Tx"
   318  	}
   319  	lines := strings.Split(string(p), "\r\n")
   320  	if lines[len(lines)-1] == "" {
   321  		lines = lines[:len(lines)-1]
   322  	}
   323  	for _, line := range lines {
   324  		if !dl.auth && strings.HasPrefix(line, "PASS") {
   325  			fs.Debugf(direction, "PASS *****")
   326  			continue
   327  		}
   328  		fs.Debugf(direction, "%q", line)
   329  	}
   330  	return len(p), nil
   331  }
   332  
   333  // Return a *textproto.Error if err contains one or nil otherwise
   334  func textprotoError(err error) (errX *textproto.Error) {
   335  	if errors.As(err, &errX) {
   336  		return errX
   337  	}
   338  	return nil
   339  }
   340  
   341  // returns true if this FTP error should be retried
   342  func isRetriableFtpError(err error) bool {
   343  	if errX := textprotoError(err); errX != nil {
   344  		switch errX.Code {
   345  		case ftp.StatusNotAvailable, ftp.StatusTransfertAborted:
   346  			return true
   347  		}
   348  	}
   349  	return false
   350  }
   351  
   352  // shouldRetry returns a boolean as to whether this err deserve to be
   353  // retried.  It returns the err as a convenience
   354  func shouldRetry(ctx context.Context, err error) (bool, error) {
   355  	if fserrors.ContextError(ctx, &err) {
   356  		return false, err
   357  	}
   358  	if isRetriableFtpError(err) {
   359  		return true, err
   360  	}
   361  	return fserrors.ShouldRetry(err), err
   362  }
   363  
   364  // Get a TLS config with a unique session cache.
   365  //
   366  // We can't share session caches between connections.
   367  //
   368  // See: https://github.com/artpar/rclone/issues/7234
   369  func (f *Fs) tlsConfig() *tls.Config {
   370  	var tlsConfig *tls.Config
   371  	if f.opt.TLS || f.opt.ExplicitTLS {
   372  		tlsConfig = &tls.Config{
   373  			ServerName:         f.opt.Host,
   374  			InsecureSkipVerify: f.opt.SkipVerifyTLSCert,
   375  		}
   376  		if f.opt.TLSCacheSize > 0 {
   377  			tlsConfig.ClientSessionCache = tls.NewLRUClientSessionCache(f.opt.TLSCacheSize)
   378  		}
   379  		if f.opt.DisableTLS13 {
   380  			tlsConfig.MaxVersion = tls.VersionTLS12
   381  		}
   382  	}
   383  	return tlsConfig
   384  }
   385  
   386  // Open a new connection to the FTP server.
   387  func (f *Fs) ftpConnection(ctx context.Context) (c *ftp.ServerConn, err error) {
   388  	fs.Debugf(f, "Connecting to FTP server")
   389  
   390  	// tls.Config for this connection only. Will be used for data
   391  	// and control connections.
   392  	tlsConfig := f.tlsConfig()
   393  
   394  	// Make ftp library dial with fshttp dialer optionally using TLS
   395  	initialConnection := true
   396  	dial := func(network, address string) (conn net.Conn, err error) {
   397  		fs.Debugf(f, "dial(%q,%q)", network, address)
   398  		defer func() {
   399  			fs.Debugf(f, "> dial: conn=%T, err=%v", conn, err)
   400  		}()
   401  		baseDialer := fshttp.NewDialer(ctx)
   402  		if f.opt.SocksProxy != "" {
   403  			conn, err = proxy.SOCKS5Dial(network, address, f.opt.SocksProxy, baseDialer)
   404  		} else {
   405  			conn, err = baseDialer.Dial(network, address)
   406  		}
   407  		if err != nil {
   408  			return nil, err
   409  		}
   410  		// Connect using cleartext only for non TLS
   411  		if tlsConfig == nil {
   412  			return conn, nil
   413  		}
   414  		// Initial connection only needs to be cleartext for explicit TLS
   415  		if f.opt.ExplicitTLS && initialConnection {
   416  			initialConnection = false
   417  			return conn, nil
   418  		}
   419  		// Upgrade connection to TLS
   420  		tlsConn := tls.Client(conn, tlsConfig)
   421  		// Do the initial handshake - tls.Client doesn't do it for us
   422  		// If we do this then connections to proftpd/pureftpd lock up
   423  		// See: https://github.com/artpar/rclone/issues/6426
   424  		// See: https://github.com/jlaffaye/ftp/issues/282
   425  		if false {
   426  			err = tlsConn.HandshakeContext(ctx)
   427  			if err != nil {
   428  				_ = conn.Close()
   429  				return nil, err
   430  			}
   431  		}
   432  		return tlsConn, nil
   433  	}
   434  	ftpConfig := []ftp.DialOption{
   435  		ftp.DialWithContext(ctx),
   436  		ftp.DialWithDialFunc(dial),
   437  	}
   438  
   439  	if f.opt.TLS {
   440  		// Our dialer takes care of TLS but ftp library also needs tlsConf
   441  		// as a trigger for sending PSBZ and PROT options to server.
   442  		ftpConfig = append(ftpConfig, ftp.DialWithTLS(tlsConfig))
   443  	} else if f.opt.ExplicitTLS {
   444  		ftpConfig = append(ftpConfig, ftp.DialWithExplicitTLS(tlsConfig))
   445  	}
   446  	if f.opt.DisableEPSV {
   447  		ftpConfig = append(ftpConfig, ftp.DialWithDisabledEPSV(true))
   448  	}
   449  	if f.opt.DisableMLSD {
   450  		ftpConfig = append(ftpConfig, ftp.DialWithDisabledMLSD(true))
   451  	}
   452  	if f.opt.DisableUTF8 {
   453  		ftpConfig = append(ftpConfig, ftp.DialWithDisabledUTF8(true))
   454  	}
   455  	if f.opt.ShutTimeout != 0 && f.opt.ShutTimeout != fs.DurationOff {
   456  		ftpConfig = append(ftpConfig, ftp.DialWithShutTimeout(time.Duration(f.opt.ShutTimeout)))
   457  	}
   458  	if f.opt.WritingMDTM {
   459  		ftpConfig = append(ftpConfig, ftp.DialWithWritingMDTM(true))
   460  	}
   461  	if f.opt.ForceListHidden {
   462  		ftpConfig = append(ftpConfig, ftp.DialWithForceListHidden(true))
   463  	}
   464  	if f.ci.Dump&(fs.DumpHeaders|fs.DumpBodies|fs.DumpRequests|fs.DumpResponses) != 0 {
   465  		ftpConfig = append(ftpConfig, ftp.DialWithDebugOutput(&debugLog{auth: f.ci.Dump&fs.DumpAuth != 0}))
   466  	}
   467  	err = f.pacer.Call(func() (bool, error) {
   468  		c, err = ftp.Dial(f.dialAddr, ftpConfig...)
   469  		if err != nil {
   470  			return shouldRetry(ctx, err)
   471  		}
   472  		err = c.Login(f.user, f.pass)
   473  		if err != nil {
   474  			_ = c.Quit()
   475  			return shouldRetry(ctx, err)
   476  		}
   477  		return false, nil
   478  	})
   479  	if err != nil {
   480  		err = fmt.Errorf("failed to make FTP connection to %q: %w", f.dialAddr, err)
   481  	}
   482  	return c, err
   483  }
   484  
   485  // Get an FTP connection from the pool, or open a new one
   486  func (f *Fs) getFtpConnection(ctx context.Context) (c *ftp.ServerConn, err error) {
   487  	if f.opt.Concurrency > 0 {
   488  		f.tokens.Get()
   489  	}
   490  	accounting.LimitTPS(ctx)
   491  	f.poolMu.Lock()
   492  	if len(f.pool) > 0 {
   493  		c = f.pool[0]
   494  		f.pool = f.pool[1:]
   495  	}
   496  	f.poolMu.Unlock()
   497  	if c != nil {
   498  		return c, nil
   499  	}
   500  	c, err = f.ftpConnection(ctx)
   501  	if err != nil && f.opt.Concurrency > 0 {
   502  		f.tokens.Put()
   503  	}
   504  	return c, err
   505  }
   506  
   507  // Return an FTP connection to the pool
   508  //
   509  // It nils the pointed to connection out so it can't be reused
   510  //
   511  // if err is not nil then it checks the connection is alive using a
   512  // NOOP request
   513  func (f *Fs) putFtpConnection(pc **ftp.ServerConn, err error) {
   514  	if f.opt.Concurrency > 0 {
   515  		defer f.tokens.Put()
   516  	}
   517  	if pc == nil {
   518  		return
   519  	}
   520  	c := *pc
   521  	if c == nil {
   522  		return
   523  	}
   524  	*pc = nil
   525  	if err != nil {
   526  		// If not a regular FTP error code then check the connection
   527  		if tpErr := textprotoError(err); tpErr != nil {
   528  			nopErr := c.NoOp()
   529  			if nopErr != nil {
   530  				fs.Debugf(f, "Connection failed, closing: %v", nopErr)
   531  				_ = c.Quit()
   532  				return
   533  			}
   534  		}
   535  	}
   536  	f.poolMu.Lock()
   537  	f.pool = append(f.pool, c)
   538  	if f.opt.IdleTimeout > 0 {
   539  		f.drain.Reset(time.Duration(f.opt.IdleTimeout)) // nudge on the pool emptying timer
   540  	}
   541  	f.poolMu.Unlock()
   542  }
   543  
   544  // Drain the pool of any connections
   545  func (f *Fs) drainPool(ctx context.Context) (err error) {
   546  	f.poolMu.Lock()
   547  	defer f.poolMu.Unlock()
   548  	if f.opt.IdleTimeout > 0 {
   549  		f.drain.Stop()
   550  	}
   551  	if len(f.pool) != 0 {
   552  		fs.Debugf(f, "closing %d unused connections", len(f.pool))
   553  	}
   554  	for i, c := range f.pool {
   555  		if cErr := c.Quit(); cErr != nil {
   556  			err = cErr
   557  		}
   558  		f.pool[i] = nil
   559  	}
   560  	f.pool = nil
   561  	return err
   562  }
   563  
   564  // NewFs constructs an Fs from the path, container:path
   565  func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (ff fs.Fs, err error) {
   566  	// defer fs.Trace(nil, "name=%q, root=%q", name, root)("fs=%v, err=%v", &ff, &err)
   567  	// Parse config into Options struct
   568  	opt := new(Options)
   569  	err = configstruct.Set(m, opt)
   570  	if err != nil {
   571  		return nil, err
   572  	}
   573  	pass := ""
   574  	if opt.AskPassword && opt.Pass == "" {
   575  		pass = config.GetPassword("FTP server password")
   576  	} else {
   577  		pass, err = obscure.Reveal(opt.Pass)
   578  		if err != nil {
   579  			return nil, fmt.Errorf("NewFS decrypt password: %w", err)
   580  		}
   581  	}
   582  	user := opt.User
   583  	if user == "" {
   584  		user = currentUser
   585  	}
   586  	port := opt.Port
   587  	if port == "" {
   588  		port = "21"
   589  	}
   590  
   591  	dialAddr := opt.Host + ":" + port
   592  	protocol := "ftp://"
   593  	if opt.TLS {
   594  		protocol = "ftps://"
   595  	}
   596  	if opt.TLS && opt.ExplicitTLS {
   597  		return nil, errors.New("implicit TLS and explicit TLS are mutually incompatible, please revise your config")
   598  	}
   599  	u := protocol + path.Join(dialAddr+"/", root)
   600  	ci := fs.GetConfig(ctx)
   601  	f := &Fs{
   602  		name:     name,
   603  		root:     root,
   604  		opt:      *opt,
   605  		ci:       ci,
   606  		url:      u,
   607  		user:     user,
   608  		pass:     pass,
   609  		dialAddr: dialAddr,
   610  		tokens:   pacer.NewTokenDispenser(opt.Concurrency),
   611  		pacer:    fs.NewPacer(ctx, pacer.NewDefault(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant))),
   612  	}
   613  	f.features = (&fs.Features{
   614  		CanHaveEmptyDirectories: true,
   615  		PartialUploads:          true,
   616  	}).Fill(ctx, f)
   617  	// set the pool drainer timer going
   618  	if f.opt.IdleTimeout > 0 {
   619  		f.drain = time.AfterFunc(time.Duration(opt.IdleTimeout), func() { _ = f.drainPool(ctx) })
   620  	}
   621  	// Make a connection and pool it to return errors early
   622  	c, err := f.getFtpConnection(ctx)
   623  	if err != nil {
   624  		return nil, fmt.Errorf("NewFs: %w", err)
   625  	}
   626  	f.fGetTime = c.IsGetTimeSupported()
   627  	f.fSetTime = c.IsSetTimeSupported()
   628  	f.fLstTime = c.IsTimePreciseInList()
   629  	if !f.fLstTime && f.fGetTime {
   630  		f.features.SlowModTime = true
   631  	}
   632  	f.putFtpConnection(&c, nil)
   633  	if root != "" {
   634  		// Check to see if the root actually an existing file
   635  		remote := path.Base(root)
   636  		f.root = path.Dir(root)
   637  		if f.root == "." {
   638  			f.root = ""
   639  		}
   640  		_, err := f.NewObject(ctx, remote)
   641  		if err != nil {
   642  			if err == fs.ErrorObjectNotFound || errors.Is(err, fs.ErrorNotAFile) {
   643  				// File doesn't exist so return old f
   644  				f.root = root
   645  				return f, nil
   646  			}
   647  			return nil, err
   648  		}
   649  		// return an error with an fs which points to the parent
   650  		return f, fs.ErrorIsFile
   651  	}
   652  	return f, err
   653  }
   654  
   655  // Shutdown the backend, closing any background tasks and any
   656  // cached connections.
   657  func (f *Fs) Shutdown(ctx context.Context) error {
   658  	return f.drainPool(ctx)
   659  }
   660  
   661  // translateErrorFile turns FTP errors into rclone errors if possible for a file
   662  func translateErrorFile(err error) error {
   663  	if errX := textprotoError(err); errX != nil {
   664  		switch errX.Code {
   665  		case ftp.StatusFileUnavailable, ftp.StatusFileActionIgnored:
   666  			err = fs.ErrorObjectNotFound
   667  		}
   668  	}
   669  	return err
   670  }
   671  
   672  // translateErrorDir turns FTP errors into rclone errors if possible for a directory
   673  func translateErrorDir(err error) error {
   674  	if errX := textprotoError(err); errX != nil {
   675  		switch errX.Code {
   676  		case ftp.StatusFileUnavailable, ftp.StatusFileActionIgnored:
   677  			err = fs.ErrorDirNotFound
   678  		}
   679  	}
   680  	return err
   681  }
   682  
   683  // entryToStandard converts an incoming ftp.Entry to Standard encoding
   684  func (f *Fs) entryToStandard(entry *ftp.Entry) {
   685  	// Skip . and .. as we don't want these encoded
   686  	if entry.Name == "." || entry.Name == ".." {
   687  		return
   688  	}
   689  	entry.Name = f.opt.Enc.ToStandardName(entry.Name)
   690  	entry.Target = f.opt.Enc.ToStandardPath(entry.Target)
   691  }
   692  
   693  // dirFromStandardPath returns dir in encoded form.
   694  func (f *Fs) dirFromStandardPath(dir string) string {
   695  	// Skip . and .. as we don't want these encoded
   696  	if dir == "." || dir == ".." {
   697  		return dir
   698  	}
   699  	return f.opt.Enc.FromStandardPath(dir)
   700  }
   701  
   702  // findItem finds a directory entry for the name in its parent directory
   703  func (f *Fs) findItem(ctx context.Context, remote string) (entry *ftp.Entry, err error) {
   704  	// defer fs.Trace(remote, "")("o=%v, err=%v", &o, &err)
   705  	if remote == "" || remote == "." || remote == "/" {
   706  		// if root, assume exists and synthesize an entry
   707  		return &ftp.Entry{
   708  			Name: "",
   709  			Type: ftp.EntryTypeFolder,
   710  			Time: time.Now(),
   711  		}, nil
   712  	}
   713  
   714  	c, err := f.getFtpConnection(ctx)
   715  	if err != nil {
   716  		return nil, fmt.Errorf("findItem: %w", err)
   717  	}
   718  
   719  	// returns TRUE if MLST is supported which is required to call GetEntry
   720  	if c.IsTimePreciseInList() {
   721  		entry, err := c.GetEntry(f.opt.Enc.FromStandardPath(remote))
   722  		f.putFtpConnection(&c, err)
   723  		if err != nil {
   724  			err = translateErrorFile(err)
   725  			if err == fs.ErrorObjectNotFound {
   726  				return nil, nil
   727  			}
   728  			if errX := textprotoError(err); errX != nil {
   729  				switch errX.Code {
   730  				case ftp.StatusBadArguments:
   731  					err = nil
   732  				}
   733  			}
   734  			return nil, err
   735  		}
   736  		if entry != nil {
   737  			f.entryToStandard(entry)
   738  		}
   739  		return entry, nil
   740  	}
   741  
   742  	dir := path.Dir(remote)
   743  	base := path.Base(remote)
   744  
   745  	files, err := c.List(f.dirFromStandardPath(dir))
   746  	f.putFtpConnection(&c, err)
   747  	if err != nil {
   748  		return nil, translateErrorFile(err)
   749  	}
   750  	for _, file := range files {
   751  		f.entryToStandard(file)
   752  		if file.Name == base {
   753  			return file, nil
   754  		}
   755  	}
   756  	return nil, nil
   757  }
   758  
   759  // NewObject finds the Object at remote.  If it can't be found
   760  // it returns the error fs.ErrorObjectNotFound.
   761  func (f *Fs) NewObject(ctx context.Context, remote string) (o fs.Object, err error) {
   762  	// defer fs.Trace(remote, "")("o=%v, err=%v", &o, &err)
   763  	entry, err := f.findItem(ctx, path.Join(f.root, remote))
   764  	if err != nil {
   765  		return nil, err
   766  	}
   767  	if entry != nil && entry.Type != ftp.EntryTypeFolder {
   768  		o := &Object{
   769  			fs:     f,
   770  			remote: remote,
   771  		}
   772  		o.info = &FileInfo{
   773  			Name:    remote,
   774  			Size:    entry.Size,
   775  			ModTime: entry.Time,
   776  			precise: f.fLstTime,
   777  		}
   778  		return o, nil
   779  	}
   780  	return nil, fs.ErrorObjectNotFound
   781  }
   782  
   783  // dirExists checks the directory pointed to by remote exists or not
   784  func (f *Fs) dirExists(ctx context.Context, remote string) (exists bool, err error) {
   785  	entry, err := f.findItem(ctx, path.Join(f.root, remote))
   786  	if err != nil {
   787  		return false, fmt.Errorf("dirExists: %w", err)
   788  	}
   789  	if entry != nil && entry.Type == ftp.EntryTypeFolder {
   790  		return true, nil
   791  	}
   792  	return false, nil
   793  }
   794  
   795  // List the objects and directories in dir into entries.  The
   796  // entries can be returned in any order but should be for a
   797  // complete directory.
   798  //
   799  // dir should be "" to list the root, and should not have
   800  // trailing slashes.
   801  //
   802  // This should return ErrDirNotFound if the directory isn't
   803  // found.
   804  func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
   805  	// defer log.Trace(dir, "dir=%q", dir)("entries=%v, err=%v", &entries, &err)
   806  	c, err := f.getFtpConnection(ctx)
   807  	if err != nil {
   808  		return nil, fmt.Errorf("list: %w", err)
   809  	}
   810  
   811  	var listErr error
   812  	var files []*ftp.Entry
   813  
   814  	resultchan := make(chan []*ftp.Entry, 1)
   815  	errchan := make(chan error, 1)
   816  	go func() {
   817  		result, err := c.List(f.dirFromStandardPath(path.Join(f.root, dir)))
   818  		f.putFtpConnection(&c, err)
   819  		if err != nil {
   820  			errchan <- err
   821  			return
   822  		}
   823  		resultchan <- result
   824  	}()
   825  
   826  	// Wait for List for up to Timeout seconds
   827  	timer := time.NewTimer(f.ci.TimeoutOrInfinite())
   828  	select {
   829  	case listErr = <-errchan:
   830  		timer.Stop()
   831  		return nil, translateErrorDir(listErr)
   832  	case files = <-resultchan:
   833  		timer.Stop()
   834  	case <-timer.C:
   835  		// if timer fired assume no error but connection dead
   836  		fs.Errorf(f, "Timeout when waiting for List")
   837  		return nil, errors.New("timeout when waiting for List")
   838  	}
   839  
   840  	// Annoyingly FTP returns success for a directory which
   841  	// doesn't exist, so check it really doesn't exist if no
   842  	// entries found.
   843  	if len(files) == 0 {
   844  		exists, err := f.dirExists(ctx, dir)
   845  		if err != nil {
   846  			return nil, fmt.Errorf("list: %w", err)
   847  		}
   848  		if !exists {
   849  			return nil, fs.ErrorDirNotFound
   850  		}
   851  	}
   852  	for i := range files {
   853  		object := files[i]
   854  		f.entryToStandard(object)
   855  		newremote := path.Join(dir, object.Name)
   856  		switch object.Type {
   857  		case ftp.EntryTypeFolder:
   858  			if object.Name == "." || object.Name == ".." {
   859  				continue
   860  			}
   861  			d := fs.NewDir(newremote, object.Time)
   862  			entries = append(entries, d)
   863  		default:
   864  			o := &Object{
   865  				fs:     f,
   866  				remote: newremote,
   867  			}
   868  			info := &FileInfo{
   869  				Name:    newremote,
   870  				Size:    object.Size,
   871  				ModTime: object.Time,
   872  				precise: f.fLstTime,
   873  			}
   874  			o.info = info
   875  			entries = append(entries, o)
   876  		}
   877  	}
   878  	return entries, nil
   879  }
   880  
   881  // Hashes are not supported
   882  func (f *Fs) Hashes() hash.Set {
   883  	return 0
   884  }
   885  
   886  // Precision shows whether modified time is supported or not depending on the
   887  // FTP server capabilities, namely whether FTP server:
   888  //   - accepts the MDTM command to get file time (fGetTime)
   889  //     or supports MLSD returning precise file time in the list (fLstTime)
   890  //   - accepts the MFMT command to set file time (fSetTime)
   891  //     or non-standard form of the MDTM command (fSetTime, too)
   892  //     used by VsFtpd for the same purpose (WritingMDTM)
   893  //
   894  // See "mdtm_write" in https://security.appspot.com/vsftpd/vsftpd_conf.html
   895  func (f *Fs) Precision() time.Duration {
   896  	if (f.fGetTime || f.fLstTime) && f.fSetTime {
   897  		return time.Second
   898  	}
   899  	return fs.ModTimeNotSupported
   900  }
   901  
   902  // Put in to the remote path with the modTime given of the given size
   903  //
   904  // May create the object even if it returns an error - if so
   905  // will return the object and the error, otherwise will return
   906  // nil and the error
   907  func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   908  	// fs.Debugf(f, "Trying to put file %s", src.Remote())
   909  	err := f.mkParentDir(ctx, src.Remote())
   910  	if err != nil {
   911  		return nil, fmt.Errorf("Put mkParentDir failed: %w", err)
   912  	}
   913  	o := &Object{
   914  		fs:     f,
   915  		remote: src.Remote(),
   916  	}
   917  	err = o.Update(ctx, in, src, options...)
   918  	return o, err
   919  }
   920  
   921  // PutStream uploads to the remote path with the modTime given of indeterminate size
   922  func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   923  	return f.Put(ctx, in, src, options...)
   924  }
   925  
   926  // getInfo reads the FileInfo for a path
   927  func (f *Fs) getInfo(ctx context.Context, remote string) (fi *FileInfo, err error) {
   928  	// defer fs.Trace(remote, "")("fi=%v, err=%v", &fi, &err)
   929  	file, err := f.findItem(ctx, remote)
   930  	if err != nil {
   931  		return nil, err
   932  	} else if file != nil {
   933  		info := &FileInfo{
   934  			Name:    remote,
   935  			Size:    file.Size,
   936  			ModTime: file.Time,
   937  			precise: f.fLstTime,
   938  			IsDir:   file.Type == ftp.EntryTypeFolder,
   939  		}
   940  		return info, nil
   941  	}
   942  	return nil, fs.ErrorObjectNotFound
   943  }
   944  
   945  // mkdir makes the directory and parents using unrooted paths
   946  func (f *Fs) mkdir(ctx context.Context, abspath string) error {
   947  	abspath = path.Clean(abspath)
   948  	if abspath == "." || abspath == "/" {
   949  		return nil
   950  	}
   951  	fi, err := f.getInfo(ctx, abspath)
   952  	if err == nil {
   953  		if fi.IsDir {
   954  			return nil
   955  		}
   956  		return fs.ErrorIsFile
   957  	} else if err != fs.ErrorObjectNotFound {
   958  		return fmt.Errorf("mkdir %q failed: %w", abspath, err)
   959  	}
   960  	parent := path.Dir(abspath)
   961  	err = f.mkdir(ctx, parent)
   962  	if err != nil {
   963  		return err
   964  	}
   965  	c, connErr := f.getFtpConnection(ctx)
   966  	if connErr != nil {
   967  		return fmt.Errorf("mkdir: %w", connErr)
   968  	}
   969  	err = c.MakeDir(f.dirFromStandardPath(abspath))
   970  	f.putFtpConnection(&c, err)
   971  	if errX := textprotoError(err); errX != nil {
   972  		switch errX.Code {
   973  		case ftp.StatusRequestedFileActionOK: // some ftp servers apparently return 250 instead of 257
   974  			err = nil // see: https://forum.rclone.org/t/rclone-pop-up-an-i-o-error-when-creating-a-folder-in-a-mounted-ftp-drive/44368/
   975  		case ftp.StatusFileUnavailable: // dir already exists: see issue #2181
   976  			err = nil
   977  		case 521: // dir already exists: error number according to RFC 959: issue #2363
   978  			err = nil
   979  		}
   980  	}
   981  	return err
   982  }
   983  
   984  // mkParentDir makes the parent of remote if necessary and any
   985  // directories above that
   986  func (f *Fs) mkParentDir(ctx context.Context, remote string) error {
   987  	parent := path.Dir(remote)
   988  	return f.mkdir(ctx, path.Join(f.root, parent))
   989  }
   990  
   991  // Mkdir creates the directory if it doesn't exist
   992  func (f *Fs) Mkdir(ctx context.Context, dir string) (err error) {
   993  	// defer fs.Trace(dir, "")("err=%v", &err)
   994  	root := path.Join(f.root, dir)
   995  	return f.mkdir(ctx, root)
   996  }
   997  
   998  // Rmdir removes the directory (container, bucket) if empty
   999  //
  1000  // Return an error if it doesn't exist or isn't empty
  1001  func (f *Fs) Rmdir(ctx context.Context, dir string) error {
  1002  	c, err := f.getFtpConnection(ctx)
  1003  	if err != nil {
  1004  		return fmt.Errorf("Rmdir: %w", translateErrorFile(err))
  1005  	}
  1006  	err = c.RemoveDir(f.dirFromStandardPath(path.Join(f.root, dir)))
  1007  	f.putFtpConnection(&c, err)
  1008  	return translateErrorDir(err)
  1009  }
  1010  
  1011  // Move renames a remote file object
  1012  func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
  1013  	srcObj, ok := src.(*Object)
  1014  	if !ok {
  1015  		fs.Debugf(src, "Can't move - not same remote type")
  1016  		return nil, fs.ErrorCantMove
  1017  	}
  1018  	err := f.mkParentDir(ctx, remote)
  1019  	if err != nil {
  1020  		return nil, fmt.Errorf("Move mkParentDir failed: %w", err)
  1021  	}
  1022  	c, err := f.getFtpConnection(ctx)
  1023  	if err != nil {
  1024  		return nil, fmt.Errorf("Move: %w", err)
  1025  	}
  1026  	err = c.Rename(
  1027  		f.opt.Enc.FromStandardPath(path.Join(srcObj.fs.root, srcObj.remote)),
  1028  		f.opt.Enc.FromStandardPath(path.Join(f.root, remote)),
  1029  	)
  1030  	f.putFtpConnection(&c, err)
  1031  	if err != nil {
  1032  		return nil, fmt.Errorf("Move Rename failed: %w", err)
  1033  	}
  1034  	dstObj, err := f.NewObject(ctx, remote)
  1035  	if err != nil {
  1036  		return nil, fmt.Errorf("Move NewObject failed: %w", err)
  1037  	}
  1038  	return dstObj, nil
  1039  }
  1040  
  1041  // DirMove moves src, srcRemote to this remote at dstRemote
  1042  // using server-side move operations.
  1043  //
  1044  // Will only be called if src.Fs().Name() == f.Name()
  1045  //
  1046  // If it isn't possible then return fs.ErrorCantDirMove
  1047  //
  1048  // If destination exists then return fs.ErrorDirExists
  1049  func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string) error {
  1050  	srcFs, ok := src.(*Fs)
  1051  	if !ok {
  1052  		fs.Debugf(srcFs, "Can't move directory - not same remote type")
  1053  		return fs.ErrorCantDirMove
  1054  	}
  1055  	srcPath := path.Join(srcFs.root, srcRemote)
  1056  	dstPath := path.Join(f.root, dstRemote)
  1057  
  1058  	// Check if destination exists
  1059  	fi, err := f.getInfo(ctx, dstPath)
  1060  	if err == nil {
  1061  		if fi.IsDir {
  1062  			return fs.ErrorDirExists
  1063  		}
  1064  		return fs.ErrorIsFile
  1065  	} else if err != fs.ErrorObjectNotFound {
  1066  		return fmt.Errorf("DirMove getInfo failed: %w", err)
  1067  	}
  1068  
  1069  	// Make sure the parent directory exists
  1070  	err = f.mkdir(ctx, path.Dir(dstPath))
  1071  	if err != nil {
  1072  		return fmt.Errorf("DirMove mkParentDir dst failed: %w", err)
  1073  	}
  1074  
  1075  	// Do the move
  1076  	c, err := f.getFtpConnection(ctx)
  1077  	if err != nil {
  1078  		return fmt.Errorf("DirMove: %w", err)
  1079  	}
  1080  	err = c.Rename(
  1081  		f.dirFromStandardPath(srcPath),
  1082  		f.dirFromStandardPath(dstPath),
  1083  	)
  1084  	f.putFtpConnection(&c, err)
  1085  	if err != nil {
  1086  		return fmt.Errorf("DirMove Rename(%q,%q) failed: %w", srcPath, dstPath, err)
  1087  	}
  1088  	return nil
  1089  }
  1090  
  1091  // ------------------------------------------------------------
  1092  
  1093  // Fs returns the parent Fs
  1094  func (o *Object) Fs() fs.Info {
  1095  	return o.fs
  1096  }
  1097  
  1098  // String version of o
  1099  func (o *Object) String() string {
  1100  	if o == nil {
  1101  		return "<nil>"
  1102  	}
  1103  	return o.remote
  1104  }
  1105  
  1106  // Remote returns the remote path
  1107  func (o *Object) Remote() string {
  1108  	return o.remote
  1109  }
  1110  
  1111  // Hash returns the hash of an object returning a lowercase hex string
  1112  func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) {
  1113  	return "", hash.ErrUnsupported
  1114  }
  1115  
  1116  // Size returns the size of an object in bytes
  1117  func (o *Object) Size() int64 {
  1118  	return int64(o.info.Size)
  1119  }
  1120  
  1121  // ModTime returns the modification time of the object
  1122  func (o *Object) ModTime(ctx context.Context) time.Time {
  1123  	if !o.info.precise && o.fs.fGetTime {
  1124  		c, err := o.fs.getFtpConnection(ctx)
  1125  		if err == nil {
  1126  			path := path.Join(o.fs.root, o.remote)
  1127  			path = o.fs.opt.Enc.FromStandardPath(path)
  1128  			modTime, err := c.GetTime(path)
  1129  			if err == nil && o.info != nil {
  1130  				o.info.ModTime = modTime
  1131  				o.info.precise = true
  1132  			}
  1133  			o.fs.putFtpConnection(&c, err)
  1134  		}
  1135  	}
  1136  	return o.info.ModTime
  1137  }
  1138  
  1139  // SetModTime sets the modification time of the object
  1140  func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error {
  1141  	if !o.fs.fSetTime {
  1142  		fs.Debugf(o.fs, "SetModTime is not supported")
  1143  		return nil
  1144  	}
  1145  	c, err := o.fs.getFtpConnection(ctx)
  1146  	if err != nil {
  1147  		return err
  1148  	}
  1149  	path := path.Join(o.fs.root, o.remote)
  1150  	path = o.fs.opt.Enc.FromStandardPath(path)
  1151  	err = c.SetTime(path, modTime.In(time.UTC))
  1152  	if err == nil && o.info != nil {
  1153  		o.info.ModTime = modTime
  1154  		o.info.precise = true
  1155  	}
  1156  	o.fs.putFtpConnection(&c, err)
  1157  	return err
  1158  }
  1159  
  1160  // Storable returns a boolean as to whether this object is storable
  1161  func (o *Object) Storable() bool {
  1162  	return true
  1163  }
  1164  
  1165  // ftpReadCloser implements io.ReadCloser for FTP objects.
  1166  type ftpReadCloser struct {
  1167  	rc  io.ReadCloser
  1168  	c   *ftp.ServerConn
  1169  	f   *Fs
  1170  	err error // errors found during read
  1171  }
  1172  
  1173  // Read bytes into p
  1174  func (f *ftpReadCloser) Read(p []byte) (n int, err error) {
  1175  	n, err = f.rc.Read(p)
  1176  	if err != nil && err != io.EOF {
  1177  		f.err = err // store any errors for Close to examine
  1178  	}
  1179  	return
  1180  }
  1181  
  1182  // Close the FTP reader and return the connection to the pool
  1183  func (f *ftpReadCloser) Close() error {
  1184  	var err error
  1185  	errchan := make(chan error, 1)
  1186  	go func() {
  1187  		errchan <- f.rc.Close()
  1188  	}()
  1189  	// Wait for Close for up to 60 seconds by default
  1190  	closeTimeout := f.f.opt.CloseTimeout
  1191  	if closeTimeout == 0 {
  1192  		closeTimeout = fs.DurationOff
  1193  	}
  1194  	timer := time.NewTimer(time.Duration(closeTimeout))
  1195  	select {
  1196  	case err = <-errchan:
  1197  		timer.Stop()
  1198  	case <-timer.C:
  1199  		// if timer fired assume no error but connection dead
  1200  		fs.Errorf(f.f, "Timeout when waiting for connection Close")
  1201  		f.f.putFtpConnection(nil, nil)
  1202  		return nil
  1203  	}
  1204  	// if errors while reading or closing, dump the connection
  1205  	if err != nil || f.err != nil {
  1206  		_ = f.c.Quit()
  1207  		f.f.putFtpConnection(nil, nil)
  1208  	} else {
  1209  		f.f.putFtpConnection(&f.c, nil)
  1210  	}
  1211  	// mask the error if it was caused by a premature close
  1212  	// NB StatusAboutToSend is to work around a bug in pureftpd
  1213  	// See: https://github.com/artpar/rclone/issues/3445#issuecomment-521654257
  1214  	if errX := textprotoError(err); errX != nil {
  1215  		switch errX.Code {
  1216  		case ftp.StatusTransfertAborted, ftp.StatusFileUnavailable, ftp.StatusAboutToSend:
  1217  			err = nil
  1218  		}
  1219  	}
  1220  	return err
  1221  }
  1222  
  1223  // Open an object for read
  1224  func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (rc io.ReadCloser, err error) {
  1225  	// defer fs.Trace(o, "")("rc=%v, err=%v", &rc, &err)
  1226  	path := path.Join(o.fs.root, o.remote)
  1227  	var offset, limit int64 = 0, -1
  1228  	for _, option := range options {
  1229  		switch x := option.(type) {
  1230  		case *fs.SeekOption:
  1231  			offset = x.Offset
  1232  		case *fs.RangeOption:
  1233  			offset, limit = x.Decode(o.Size())
  1234  		default:
  1235  			if option.Mandatory() {
  1236  				fs.Logf(o, "Unsupported mandatory option: %v", option)
  1237  			}
  1238  		}
  1239  	}
  1240  
  1241  	var (
  1242  		fd *ftp.Response
  1243  		c  *ftp.ServerConn
  1244  	)
  1245  	err = o.fs.pacer.Call(func() (bool, error) {
  1246  		c, err = o.fs.getFtpConnection(ctx)
  1247  		if err != nil {
  1248  			return false, err // getFtpConnection has retries already
  1249  		}
  1250  		fd, err = c.RetrFrom(o.fs.opt.Enc.FromStandardPath(path), uint64(offset))
  1251  		if err != nil {
  1252  			o.fs.putFtpConnection(&c, err)
  1253  		}
  1254  		return shouldRetry(ctx, err)
  1255  	})
  1256  	if err != nil {
  1257  		return nil, fmt.Errorf("open: %w", err)
  1258  	}
  1259  
  1260  	rc = &ftpReadCloser{rc: readers.NewLimitedReadCloser(fd, limit), c: c, f: o.fs}
  1261  	return rc, nil
  1262  }
  1263  
  1264  // Update the already existing object
  1265  //
  1266  // Copy the reader into the object updating modTime and size.
  1267  //
  1268  // The new object may have been created if an error is returned
  1269  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (err error) {
  1270  	// defer fs.Trace(o, "src=%v", src)("err=%v", &err)
  1271  	path := path.Join(o.fs.root, o.remote)
  1272  	// remove the file if upload failed
  1273  	remove := func() {
  1274  		// Give the FTP server a chance to get its internal state in order after the error.
  1275  		// The error may have been local in which case we closed the connection.  The server
  1276  		// may still be dealing with it for a moment. A sleep isn't ideal but I haven't been
  1277  		// able to think of a better method to find out if the server has finished - ncw
  1278  		time.Sleep(1 * time.Second)
  1279  		removeErr := o.Remove(ctx)
  1280  		if removeErr != nil {
  1281  			fs.Debugf(o, "Failed to remove: %v", removeErr)
  1282  		} else {
  1283  			fs.Debugf(o, "Removed after failed upload: %v", err)
  1284  		}
  1285  	}
  1286  	c, err := o.fs.getFtpConnection(ctx)
  1287  	if err != nil {
  1288  		return fmt.Errorf("Update: %w", err)
  1289  	}
  1290  	err = c.Stor(o.fs.opt.Enc.FromStandardPath(path), in)
  1291  	// Ignore error 250 here - send by some servers
  1292  	if errX := textprotoError(err); errX != nil {
  1293  		switch errX.Code {
  1294  		case ftp.StatusRequestedFileActionOK:
  1295  			err = nil
  1296  		}
  1297  	}
  1298  	if err != nil {
  1299  		_ = c.Quit() // toss this connection to avoid sync errors
  1300  		// recycle connection in advance to let remove() find free token
  1301  		o.fs.putFtpConnection(nil, err)
  1302  		remove()
  1303  		return fmt.Errorf("update stor: %w", err)
  1304  	}
  1305  	o.fs.putFtpConnection(&c, nil)
  1306  	if err = o.SetModTime(ctx, src.ModTime(ctx)); err != nil {
  1307  		return fmt.Errorf("SetModTime: %w", err)
  1308  	}
  1309  	o.info, err = o.fs.getInfo(ctx, path)
  1310  	if err != nil {
  1311  		return fmt.Errorf("update getinfo: %w", err)
  1312  	}
  1313  	return nil
  1314  }
  1315  
  1316  // Remove an object
  1317  func (o *Object) Remove(ctx context.Context) (err error) {
  1318  	// defer fs.Trace(o, "")("err=%v", &err)
  1319  	path := path.Join(o.fs.root, o.remote)
  1320  	// Check if it's a directory or a file
  1321  	info, err := o.fs.getInfo(ctx, path)
  1322  	if err != nil {
  1323  		return err
  1324  	}
  1325  	if info.IsDir {
  1326  		err = o.fs.Rmdir(ctx, o.remote)
  1327  	} else {
  1328  		c, err := o.fs.getFtpConnection(ctx)
  1329  		if err != nil {
  1330  			return fmt.Errorf("Remove: %w", err)
  1331  		}
  1332  		err = c.Delete(o.fs.opt.Enc.FromStandardPath(path))
  1333  		o.fs.putFtpConnection(&c, err)
  1334  	}
  1335  	return err
  1336  }
  1337  
  1338  // Check the interfaces are satisfied
  1339  var (
  1340  	_ fs.Fs          = &Fs{}
  1341  	_ fs.Mover       = &Fs{}
  1342  	_ fs.DirMover    = &Fs{}
  1343  	_ fs.PutStreamer = &Fs{}
  1344  	_ fs.Shutdowner  = &Fs{}
  1345  	_ fs.Object      = &Object{}
  1346  )