go-hep.org/x/hep@v0.38.1/xrootd/file.go (about) 1 // Copyright ©2018 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package xrootd // import "go-hep.org/x/hep/xrootd" 6 7 import ( 8 "context" 9 rsync "sync" 10 11 "go-hep.org/x/hep/xrootd/xrdfs" 12 "go-hep.org/x/hep/xrootd/xrdproto/read" 13 "go-hep.org/x/hep/xrootd/xrdproto/stat" 14 "go-hep.org/x/hep/xrootd/xrdproto/sync" 15 "go-hep.org/x/hep/xrootd/xrdproto/truncate" 16 "go-hep.org/x/hep/xrootd/xrdproto/verifyw" 17 "go-hep.org/x/hep/xrootd/xrdproto/write" 18 "go-hep.org/x/hep/xrootd/xrdproto/xrdclose" 19 ) 20 21 // File implements access to a content and meta information of file over XRootD. 22 type file struct { 23 fs *fileSystem 24 handle xrdfs.FileHandle 25 compression *xrdfs.FileCompression 26 27 mu rsync.RWMutex 28 info *xrdfs.EntryStat 29 sessionID string 30 } 31 32 // Compression returns the compression info. 33 func (f *file) Compression() *xrdfs.FileCompression { 34 return f.compression 35 } 36 37 // Info returns the cached stat info. 38 // Note that it may return nil if info was not yet fetched and info may be not up-to-date. 39 func (f *file) Info() *xrdfs.EntryStat { 40 return f.info 41 } 42 43 // Handle returns the file handle. 44 func (f *file) Handle() xrdfs.FileHandle { 45 return f.handle 46 } 47 48 // Close closes the file. 49 func (f *file) Close(ctx context.Context) error { 50 return f.do(ctx, func(ctx context.Context, sid string) (string, error) { 51 return f.fs.c.sendSession(ctx, sid, nil, &xrdclose.Request{Handle: f.handle}) 52 }) 53 } 54 55 // CloseVerify closes the file and checks whether the file has the provided size. 56 // A zero size suppresses the verification. 57 func (f *file) CloseVerify(ctx context.Context, size int64) error { 58 return f.do(ctx, func(ctx context.Context, sid string) (string, error) { 59 return f.fs.c.sendSession(ctx, sid, nil, &xrdclose.Request{Handle: f.handle, Size: size}) 60 }) 61 } 62 63 // Sync commits all pending writes to an open file. 64 func (f *file) Sync(ctx context.Context) error { 65 return f.do(ctx, func(ctx context.Context, sid string) (string, error) { 66 return f.fs.c.sendSession(ctx, sid, nil, &sync.Request{Handle: f.handle}) 67 }) 68 } 69 70 // ReadAtContext reads len(p) bytes into p starting at offset off. 71 func (f *file) ReadAtContext(ctx context.Context, p []byte, off int64) (n int, err error) { 72 resp := read.Response{Data: p} 73 req := &read.Request{Handle: f.handle, Offset: off, Length: int32(len(p))} 74 err = f.do(ctx, func(ctx context.Context, sid string) (string, error) { 75 return f.fs.c.sendSession(ctx, sid, &resp, req) 76 }) 77 if err != nil { 78 return 0, err 79 } 80 return len(resp.Data), nil 81 } 82 83 // ReadAt reads len(p) bytes into p starting at offset off. 84 func (f *file) ReadAt(p []byte, off int64) (n int, err error) { 85 return f.ReadAtContext(context.Background(), p, off) 86 } 87 88 // WriteAtContext writes len(p) bytes from p to the file at offset off. 89 func (f *file) WriteAtContext(ctx context.Context, p []byte, off int64) error { 90 return f.do(ctx, func(ctx context.Context, sid string) (string, error) { 91 return f.fs.c.sendSession(ctx, sid, nil, &write.Request{Handle: f.handle, Offset: off, Data: p}) 92 }) 93 } 94 95 // WriteAt writes len(p) bytes from p to the file at offset off. 96 func (f *file) WriteAt(p []byte, off int64) (n int, err error) { 97 err = f.WriteAtContext(context.Background(), p, off) 98 if err != nil { 99 return 0, err 100 } 101 return len(p), nil 102 } 103 104 // Truncate changes the size of the named file. 105 func (f *file) Truncate(ctx context.Context, size int64) error { 106 return f.do(ctx, func(ctx context.Context, sid string) (string, error) { 107 return f.fs.c.sendSession(ctx, sid, nil, &truncate.Request{Handle: f.handle, Size: size}) 108 }) 109 } 110 111 // StatVirtualFS fetches the virtual fs stat info from the XRootD server. 112 // TODO: note that calling stat with vfs and handle may be invalid. 113 // See https://github.com/xrootd/xrootd/issues/728 for the details. 114 func (f *file) StatVirtualFS(ctx context.Context) (xrdfs.VirtualFSStat, error) { 115 var resp stat.VirtualFSResponse 116 err := f.do(ctx, func(ctx context.Context, sid string) (string, error) { 117 return f.fs.c.sendSession(ctx, sid, &resp, &stat.Request{FileHandle: f.handle, Options: stat.OptionsVFS}) 118 }) 119 if err != nil { 120 return xrdfs.VirtualFSStat{}, err 121 } 122 return resp.VirtualFSStat, nil 123 } 124 125 // Stat fetches the stat info of this file from the XRootD server. 126 // Note that Stat re-fetches value returned by the Info, so after the call to Stat 127 // calls to Info may return different value than before. 128 func (f *file) Stat(ctx context.Context) (xrdfs.EntryStat, error) { 129 f.mu.RLock() 130 sid := f.sessionID 131 f.mu.RUnlock() 132 133 var resp stat.DefaultResponse 134 sid, err := f.fs.c.sendSession(ctx, sid, &resp, &stat.Request{FileHandle: f.handle}) 135 if err != nil { 136 return xrdfs.EntryStat{}, err 137 } 138 139 f.mu.Lock() 140 f.sessionID = sid 141 f.info = &resp.EntryStat 142 f.mu.Unlock() 143 144 return resp.EntryStat, nil 145 } 146 147 // VerifyWriteAt writes len(p) bytes from p to the file at offset off using crc32 verification. 148 // 149 // TODO: note that verifyw is not supported by the XRootD server. 150 // See https://github.com/xrootd/xrootd/issues/738 for the details. 151 func (f *file) VerifyWriteAt(ctx context.Context, p []byte, off int64) error { 152 return f.do(ctx, func(ctx context.Context, sid string) (string, error) { 153 return f.fs.c.sendSession(ctx, sid, nil, verifyw.NewRequestCRC32(f.handle, off, p)) 154 }) 155 } 156 157 func (f *file) do(ctx context.Context, fct func(ctx context.Context, sid string) (string, error)) error { 158 f.mu.RLock() 159 sid := f.sessionID 160 f.mu.RUnlock() 161 162 id, err := fct(ctx, sid) 163 if err != nil { 164 return err 165 } 166 167 f.mu.Lock() 168 f.sessionID = id 169 f.mu.Unlock() 170 171 return nil 172 } 173 174 var ( 175 _ xrdfs.File = (*file)(nil) 176 )