Skip to content

Commit 6749248

Browse files
authored
Merge pull request #8 from seborama/pCloudDrive-fileRead
feat: add File.Read
2 parents 78230d2 + 30d0496 commit 6749248

File tree

2 files changed

+94
-17
lines changed

2 files changed

+94
-17
lines changed

fuse/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ At this stage, this is explorative. The code base is entirely experimental, most
1212

1313
The drive can be mounted via the tests and it can be "walked" through.
1414

15-
Files contents cannot be read just yet.
16-
17-
No write operations are supported
15+
No write operations are supported for now.
1816

1917
## Change log
2018

19+
2024-Apr-23 - Added support to read file contents.
20+
2121
2024-Apr-22 - The pCloud drive can listed entirely. `ls` on the root of the mount will list directories and files contained in the root of the pCloud drive.
2222

2323
2024-Apr-21 - The pCloud drive can be mounted (via the test - see "Getting started"). `ls` on the root of the mount will list directories and files contained in the root of the pCloud drive.

fuse/mount.go

Lines changed: 91 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,10 @@ func Mount(mountpoint string, pcClient *sdk.Client) (*Drive, error) {
4949
pcClient: pcClient,
5050
uid: uint32(uid),
5151
gid: uint32(gid),
52-
rdev: 531,
5352
dirPerms: 0o750,
5453
filePerms: 0o640,
54+
dirValid: 2 * time.Second,
55+
fileValid: time.Second,
5556
},
5657
conn: conn,
5758
}, nil
@@ -70,9 +71,10 @@ type FS struct {
7071
pcClient *sdk.Client // TODO: define an interface
7172
uid uint32
7273
gid uint32
73-
rdev uint32
7474
dirPerms os.FileMode
7575
filePerms os.FileMode
76+
dirValid time.Duration
77+
fileValid time.Duration
7678
}
7779

7880
// ensure interfaces conpliance
@@ -134,7 +136,7 @@ func (d *Dir) materialiseFolder(ctx context.Context) error {
134136

135137
// TODO: is this necessary? perhaps only for the root folder?
136138
d.Attributes = fuse.Attr{
137-
Valid: time.Second,
139+
Valid: d.fs.dirValid,
138140
Inode: d.folderID,
139141
Atime: fsList.Metadata.Modified.Time,
140142
Mtime: fsList.Metadata.Modified.Time,
@@ -143,9 +145,7 @@ func (d *Dir) materialiseFolder(ctx context.Context) error {
143145
Nlink: 1, // TODO: is that right? How else can we find this value?
144146
Uid: d.fs.uid,
145147
Gid: d.fs.gid,
146-
Rdev: d.fs.rdev,
147148
}
148-
149149
d.parentFolderID = fsList.Metadata.ParentFolderID
150150
d.folderID = fsList.Metadata.FolderID
151151

@@ -154,7 +154,7 @@ func (d *Dir) materialiseFolder(ctx context.Context) error {
154154
return item.Name, &Dir{
155155
Type: fuse.DT_Dir,
156156
Attributes: fuse.Attr{
157-
Valid: time.Second,
157+
Valid: d.fs.dirValid,
158158
Inode: item.FolderID,
159159
Atime: item.Modified.Time,
160160
Mtime: item.Modified.Time,
@@ -163,9 +163,8 @@ func (d *Dir) materialiseFolder(ctx context.Context) error {
163163
Nlink: 1, // the official pCloud client can show other values that 1 - dunno how
164164
Uid: d.fs.uid,
165165
Gid: d.fs.gid,
166-
Rdev: d.fs.rdev,
167166
},
168-
Entries: nil, // will be populated by Dir.Lookup
167+
Entries: nil, // will be populated upon access by Dir.Lookup or Dir.ReadDirAll
169168
fs: d.fs,
170169
parentFolderID: item.ParentFolderID,
171170
folderID: item.FolderID,
@@ -176,7 +175,7 @@ func (d *Dir) materialiseFolder(ctx context.Context) error {
176175
Type: fuse.DT_File,
177176
// Content: content, // TODO
178177
Attributes: fuse.Attr{
179-
Valid: time.Second,
178+
Valid: d.fs.fileValid,
180179
Inode: item.FileID,
181180
Size: item.Size,
182181
Atime: item.Modified.Time,
@@ -186,8 +185,11 @@ func (d *Dir) materialiseFolder(ctx context.Context) error {
186185
Nlink: 1, // TODO: is that right? How else can we find this value?
187186
Uid: d.fs.uid,
188187
Gid: d.fs.gid,
189-
Rdev: d.fs.rdev,
190188
},
189+
fs: d.fs,
190+
folderID: item.FolderID,
191+
fileID: item.FileID,
192+
file: nil,
191193
}
192194
})
193195

@@ -257,23 +259,98 @@ type File struct {
257259
Type fuse.DirentType
258260
Content []byte
259261
Attributes fuse.Attr
262+
fs *FS
263+
folderID uint64 // TODO: not needed??
264+
fileID uint64
265+
file *sdk.File
260266
}
261267

262268
// ensure interfaces conpliance
263269
var (
264270
_ = (fs.Node)((*File)(nil))
265271
// _ = (fs.HandleWriter)((*File)(nil))
266-
_ = (fs.HandleReadAller)((*File)(nil))
272+
// _ = (fs.HandleReadAller)((*File)(nil)) // NOTE: it's best avoiding to implement this method to avoid costly memory operations with large files.
273+
_ = (fs.HandleReader)((*File)(nil))
274+
_ = (fs.NodeOpener)((*File)(nil))
275+
_ = (fs.HandleFlusher)((*File)(nil))
276+
_ = (fs.HandleReleaser)((*File)(nil))
267277
// _ = (fs.NodeSetattrer)((*File)(nil))
268278
// _ = (EntryGetter)((*File)(nil))
269279
)
270280

271-
func (f File) Attr(ctx context.Context, a *fuse.Attr) error {
281+
func (f *File) Attr(ctx context.Context, a *fuse.Attr) error {
272282
log.Println("File.Attr called")
273283
*a = f.Attributes
274284
return nil
275285
}
276286

277-
func (File) ReadAll(ctx context.Context) ([]byte, error) {
278-
return []byte(nil), nil // TODO
287+
type fileHandle struct {
288+
file *sdk.File
289+
}
290+
291+
func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
292+
log.Println("File.Open - 1 - req.ID", req.ID, " - resp.Handle:", resp.Handle)
293+
294+
// TODO: this is temporary - for now, let's focus on a read-only implementation
295+
if !req.Flags.IsReadOnly() {
296+
return nil, fuse.Errno(syscall.EACCES)
297+
}
298+
299+
file, err := f.fs.pcClient.FileOpen(ctx, 0, sdk.T4FileByID(f.fileID))
300+
if err != nil {
301+
return nil, err
302+
}
303+
304+
f.file = file
305+
log.Println("File.Open - 2 - req.ID", req.ID, " - file.FD:", file.FD)
306+
resp.Flags |= fuse.OpenKeepCache
307+
308+
return &File{
309+
Type: f.Type,
310+
Attributes: f.Attributes,
311+
fs: f.fs,
312+
folderID: f.folderID,
313+
fileID: f.fileID,
314+
file: file,
315+
}, nil
316+
}
317+
318+
func (f *File) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
319+
log.Println("File.Read called - req.ID", req.ID, " - req.Handle:", req.Handle)
320+
if f.file == nil {
321+
return syscall.ENOENT
322+
}
323+
324+
data, err := f.fs.pcClient.FilePRead(ctx, f.file.FD, uint64(req.Size), uint64(req.Offset))
325+
if err != nil {
326+
return err
327+
}
328+
resp.Data = data
329+
330+
return nil
331+
}
332+
333+
// Flush is called each time the file or directory is closed.
334+
// Because there can be multiple file descriptors referring to a
335+
// single opened file, Flush can be called multiple times.
336+
func (f *File) Flush(ctx context.Context, req *fuse.FlushRequest) error {
337+
log.Println("File.Flush called - req.ID", req.ID, " - req.Handle:", req.Handle)
338+
if f.file != nil {
339+
err := f.fs.pcClient.FileClose(ctx, f.file.FD)
340+
f.file = nil
341+
return err
342+
}
343+
344+
return nil
345+
}
346+
347+
// A ReleaseRequest asks to release (close) an open file handle.
348+
func (f *File) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
349+
if f.file != nil {
350+
err := f.fs.pcClient.FileClose(ctx, f.file.FD)
351+
f.file = nil
352+
return err
353+
}
354+
355+
return nil
279356
}

0 commit comments

Comments
 (0)