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 )