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

     1  //go:build !plan9
     2  
     3  package hdfs
     4  
     5  import (
     6  	"context"
     7  	"errors"
     8  	"io"
     9  	"path"
    10  	"time"
    11  
    12  	"github.com/artpar/rclone/fs"
    13  	"github.com/artpar/rclone/fs/hash"
    14  	"github.com/artpar/rclone/lib/readers"
    15  	"github.com/colinmarc/hdfs/v2"
    16  )
    17  
    18  // Object describes an HDFS file
    19  type Object struct {
    20  	fs      *Fs
    21  	remote  string
    22  	size    int64
    23  	modTime time.Time
    24  }
    25  
    26  // Fs returns the parent Fs
    27  func (o *Object) Fs() fs.Info {
    28  	return o.fs
    29  }
    30  
    31  // Remote returns the remote path
    32  func (o *Object) Remote() string {
    33  	return o.remote
    34  }
    35  
    36  // Size returns the size of an object in bytes
    37  func (o *Object) Size() int64 {
    38  	return o.size
    39  }
    40  
    41  // ModTime returns the modification time of the object
    42  func (o *Object) ModTime(ctx context.Context) time.Time {
    43  	return o.modTime
    44  }
    45  
    46  // SetModTime sets the modification time of the local fs object
    47  func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error {
    48  	realpath := o.fs.realpath(o.Remote())
    49  	err := o.fs.client.Chtimes(realpath, modTime, modTime)
    50  	if err != nil {
    51  		return err
    52  	}
    53  	o.modTime = modTime
    54  	return nil
    55  }
    56  
    57  // Storable returns whether this object is storable
    58  func (o *Object) Storable() bool {
    59  	return true
    60  }
    61  
    62  // Return a string version
    63  func (o *Object) String() string {
    64  	if o == nil {
    65  		return "<nil>"
    66  	}
    67  	return o.Remote()
    68  }
    69  
    70  // Hash is not supported
    71  func (o *Object) Hash(ctx context.Context, r hash.Type) (string, error) {
    72  	return "", hash.ErrUnsupported
    73  }
    74  
    75  // Open an object for read
    76  func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
    77  	realpath := o.realpath()
    78  	fs.Debugf(o.fs, "open [%s]", realpath)
    79  	f, err := o.fs.client.Open(realpath)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	var offset, limit int64 = 0, -1
    85  	for _, option := range options {
    86  		switch x := option.(type) {
    87  		case *fs.SeekOption:
    88  			offset = x.Offset
    89  		case *fs.RangeOption:
    90  			offset, limit = x.Decode(o.Size())
    91  		}
    92  	}
    93  
    94  	_, err = f.Seek(offset, io.SeekStart)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	if limit != -1 {
   100  		in = readers.NewLimitedReadCloser(f, limit)
   101  	} else {
   102  		in = f
   103  	}
   104  
   105  	return in, err
   106  }
   107  
   108  // Update object
   109  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
   110  	realpath := o.fs.realpath(o.remote)
   111  	dirname := path.Dir(realpath)
   112  	fs.Debugf(o.fs, "update [%s]", realpath)
   113  
   114  	err := o.fs.client.MkdirAll(dirname, 0755)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	_, err = o.fs.client.Stat(realpath)
   120  	if err == nil {
   121  		err = o.fs.client.Remove(realpath)
   122  		if err != nil {
   123  			return err
   124  		}
   125  	}
   126  
   127  	out, err := o.fs.client.Create(realpath)
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	cleanup := func() {
   133  		rerr := o.fs.client.Remove(realpath)
   134  		if rerr != nil {
   135  			fs.Errorf(o.fs, "failed to remove [%v]: %v", realpath, rerr)
   136  		}
   137  	}
   138  
   139  	_, err = io.Copy(out, in)
   140  	if err != nil {
   141  		cleanup()
   142  		return err
   143  	}
   144  
   145  	// If the datanodes have acknowledged all writes but not yet
   146  	// to the namenode, FileWriter.Close can return ErrReplicating
   147  	// (wrapped in an os.PathError). This indicates that all data
   148  	// has been written, but the lease is still open for the file.
   149  	//
   150  	// It is safe in this case to either ignore the error (and let
   151  	// the lease expire on its own) or to call Close multiple
   152  	// times until it completes without an error. The Java client,
   153  	// for context, always chooses to retry, with exponential
   154  	// backoff.
   155  	err = o.fs.pacer.Call(func() (bool, error) {
   156  		err := out.Close()
   157  		if err == nil {
   158  			return false, nil
   159  		}
   160  		return errors.Is(err, hdfs.ErrReplicating), err
   161  	})
   162  	if err != nil {
   163  		cleanup()
   164  		return err
   165  	}
   166  
   167  	info, err := o.fs.client.Stat(realpath)
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	err = o.SetModTime(ctx, src.ModTime(ctx))
   173  	if err != nil {
   174  		return err
   175  	}
   176  	o.size = info.Size()
   177  
   178  	return nil
   179  }
   180  
   181  // Remove an object
   182  func (o *Object) Remove(ctx context.Context) error {
   183  	realpath := o.fs.realpath(o.remote)
   184  	fs.Debugf(o.fs, "remove [%s]", realpath)
   185  	return o.fs.client.Remove(realpath)
   186  }
   187  
   188  func (o *Object) realpath() string {
   189  	return o.fs.opt.Enc.FromStandardPath(xPath(o.Fs().Root(), o.remote))
   190  }
   191  
   192  // Check the interfaces are satisfied
   193  var (
   194  	_ fs.Object = (*Object)(nil)
   195  )