go-hep.org/x/hep@v0.38.1/xrootd/xrdio/xrdio.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 xrdio provides a File type that implements various interfaces from the io package. 6 package xrdio // import "go-hep.org/x/hep/xrootd/xrdio" 7 8 import ( 9 "context" 10 "fmt" 11 "io" 12 "io/fs" 13 "os" 14 15 "go-hep.org/x/hep/xrootd" 16 "go-hep.org/x/hep/xrootd/xrdfs" 17 ) 18 19 // File wraps a xrdfs.File and implements the following interfaces: 20 // - io.Closer 21 // - io.Reader 22 // - io.Writer 23 // - io.ReaderAt 24 // - io.WriterAt 25 // - io.Seeker 26 // - fs.File 27 type File struct { 28 cli *xrootd.Client 29 fs xrdfs.FileSystem 30 f xrdfs.File 31 32 name string 33 pos int64 34 size int64 35 } 36 37 // Open opens the name file, where name is the absolute location of that file 38 // (xrootd server address and path to the file on that server.) 39 // 40 // Example: 41 // 42 // f, err := xrdio.Open("root://server.example.com:1094//some/path/to/file") 43 func Open(name string) (*File, error) { 44 urn, err := Parse(name) 45 if err != nil { 46 return nil, fmt.Errorf("could not parse %q: %w", name, err) 47 } 48 49 xrd, err := xrootd.NewClient(context.Background(), urn.Addr, urn.User) 50 if err != nil { 51 return nil, fmt.Errorf("xrdio: could not connect to xrootd server %q: %w", urn.Addr, err) 52 } 53 54 fs := xrd.FS() 55 f, err := fs.Open(context.Background(), urn.Path, xrdfs.OpenModeOwnerRead, xrdfs.OpenOptionsOpenRead) 56 if err != nil { 57 xrd.Close() 58 return nil, fmt.Errorf("xrdio: could not open %q: %w", name, err) 59 } 60 61 xf := &File{cli: xrd, fs: fs, f: f, name: urn.Path} 62 fi, err := xf.Stat() 63 if err != nil { 64 xrd.Close() 65 return nil, fmt.Errorf("xrdio: could not stat %q: %w", name, err) 66 } 67 xf.size = fi.Size() 68 69 return xf, nil 70 } 71 72 // OpenFrom opens the file name via the given filesystem handle. 73 // name is the absolute path of the wanted file on the server. 74 // 75 // Example: 76 // 77 // f, err := xrdio.OpenFrom(fs, "/some/path/to/file") 78 func OpenFrom(fs xrdfs.FileSystem, name string) (*File, error) { 79 f, err := fs.Open(context.Background(), name, xrdfs.OpenModeOwnerRead, xrdfs.OpenOptionsOpenRead) 80 if err != nil { 81 return nil, fmt.Errorf("xrdio: could not open %q: %w", name, err) 82 } 83 84 xf := &File{fs: fs, f: f, name: name} 85 fi, err := xf.Stat() 86 if err != nil { 87 return nil, fmt.Errorf("xrdio: could not stat %q: %w", name, err) 88 } 89 xf.size = fi.Size() 90 91 return xf, nil 92 } 93 94 // Name returns the name of the file. 95 func (f *File) Name() string { 96 return f.name 97 } 98 99 // Close implements io.Closer. 100 func (f *File) Close() error { 101 if f == nil { 102 return os.ErrInvalid 103 } 104 105 var ( 106 err1 = f.f.Close(context.Background()) 107 err2 error 108 ) 109 110 if f.cli != nil { 111 err2 = f.cli.Close() 112 } 113 if err1 != nil { 114 return fmt.Errorf("xrdio: could not close file %q: %w", f.name, err1) 115 } 116 if err2 != nil { 117 return fmt.Errorf("xrdio: could not close xrd-client: %w", err2) 118 } 119 return nil 120 } 121 122 // Read implements io.Reader. 123 func (f *File) Read(data []byte) (int, error) { 124 n, err := f.f.ReadAt(data, f.pos) 125 f.pos += int64(n) 126 if err != nil { 127 return n, err 128 } 129 if f.pos == f.size { 130 err = io.EOF 131 } 132 return n, err 133 } 134 135 // ReadAt implements io.ReaderAt. 136 func (f *File) ReadAt(data []byte, offset int64) (int, error) { 137 return f.f.ReadAt(data, offset) 138 } 139 140 // Write implements io.Writer. 141 func (f *File) Write(data []byte) (int, error) { 142 n, err := f.f.WriteAt(data, f.pos) 143 f.pos += int64(n) 144 return n, err 145 } 146 147 // WriteAt implements io.WriterAt. 148 func (f *File) WriteAt(data []byte, offset int64) (int, error) { 149 return f.f.WriteAt(data, offset) 150 } 151 152 // Seek implements io.Seeker 153 func (f *File) Seek(offset int64, whence int) (int64, error) { 154 var err error 155 switch whence { 156 case io.SeekStart: 157 f.pos = offset 158 case io.SeekEnd: 159 st, err := f.Stat() 160 if err != nil { 161 return 0, fmt.Errorf("xrdio: could not xrootd-stat %q: %w", f.Name(), err) 162 } 163 f.pos = st.Size() - offset 164 case io.SeekCurrent: 165 f.pos += offset 166 } 167 return f.pos, err 168 } 169 170 func (f *File) Stat() (os.FileInfo, error) { 171 v, err := f.f.Stat(context.Background()) 172 return v, err 173 } 174 175 var ( 176 _ io.Closer = (*File)(nil) 177 _ io.Reader = (*File)(nil) 178 _ io.ReaderAt = (*File)(nil) 179 _ io.Writer = (*File)(nil) 180 _ io.WriterAt = (*File)(nil) 181 _ io.Seeker = (*File)(nil) 182 _ fs.File = (*File)(nil) 183 )