Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/radFS/main.go
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ func main() {
}

//c is a fuse connection to dev/fuse
c, err := fuse.Mount(cfg.mount)
//allowother() allows any user to access the point moint -> used in chown
c, err := fuse.Mount(cfg.mount, fuse.AllowOther())

if err != nil {
log.Println(err)
Expand Down
Empty file added cmd/radFS/temp/a.txt
Empty file.
32 changes: 26 additions & 6 deletions internal/fs/dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ func (d *Dir) Attr(ctx context.Context, a *fuse.Attr) error {
a.Atime = d.atime
a.Mtime = d.mtime
a.Ctime = d.ctime
a.Uid = d.uid
a.Gid = d.gid

return nil
}
Expand All @@ -32,6 +34,17 @@ func (d *Dir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.
d.mu.Lock()
defer d.mu.Unlock()

if req.Valid.Uid() {

//if caller is not root and caller is trying to chown to uid that is not itself
Comment thread
Delta18-Git marked this conversation as resolved.
if req.Header.Uid != 0 && req.Uid != req.Header.Uid { // to get the uid of the process making the req -> checking the caller
Comment thread
Delta18-Git marked this conversation as resolved.
return syscall.EPERM
}

d.uid = req.Uid
d.ctime = time.Now()
}

if req.Valid.Atime() {
d.atime = req.Atime
}
Expand All @@ -46,6 +59,8 @@ func (d *Dir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.
resp.Attr.Atime = d.atime
resp.Attr.Mtime = d.mtime
resp.Attr.Ctime = d.ctime
resp.Attr.Uid = d.uid
resp.Attr.Gid = d.gid

return nil

Expand Down Expand Up @@ -117,6 +132,8 @@ func (d *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error
atime: time.Now(),
ctime: time.Now(),
mtime: time.Now(),
uid: req.Uid,
gid: req.Gid,
}
d.Nodes[req.Name] = newDir

Expand All @@ -141,12 +158,15 @@ func (d *Dir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.Cr
defer d.mu.Unlock()

f := &File{
inode: nextInode(),
data: []byte{},
mode: uint32(req.Mode),
atime: time.Now(),
ctime: time.Now(),
mtime: time.Now(),
inode: nextInode(),
blocks: [][]byte{},
size: 0,
mode: uint32(req.Mode),
atime: time.Now(),
ctime: time.Now(),
mtime: time.Now(),
uid: req.Uid,
gid: req.Gid,
}

if _, exists := d.Nodes[req.Name]; exists { // checking for dupes
Expand Down
122 changes: 100 additions & 22 deletions internal/fs/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@ import (
"context"
"os"
"time"
"syscall"

"bazil.org/fuse"
"bazil.org/fuse/fs"
)

const blockSize = 4096

func (f *File) Attr(ctx context.Context, a *fuse.Attr) error {
f.mu.Lock()
defer f.mu.Unlock()
a.Inode = f.inode
a.Mode = os.FileMode(f.mode)
a.Size = uint64(len(f.data))
a.Size = uint64(f.size) //using size instead of len because its [][]byte
a.Atime = f.atime
a.Mtime = f.mtime
a.Ctime = f.ctime
Expand All @@ -32,15 +35,35 @@ func (f *File) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadR
f.mu.Lock()
defer f.mu.Unlock()

if req.Offset >= int64(len(f.data)) {
// checking if reading is starting from offset that actually exists
if req.Offset >= int64(f.size) {
resp.Data = []byte{}

return nil
}

// so that we dont read past file size
end := req.Offset + int64(req.Size)
end = min(end, int64(len(f.data)))
resp.Data = f.data[req.Offset:end]
if end > int64(f.size) {
end = int64(f.size)
}

var result []byte // final fully read file as a single array that we will reeturn
offset := req.Offset

for offset < end {
blockIndex := offset / blockSize
blockOffset := offset % blockSize

// min of how much space is left on currect block and how much data is left to read -> basically safety guard
toRead := min(int64(blockSize)-blockOffset, end-offset)

result = append(result, f.blocks[blockIndex][blockOffset:blockOffset+toRead]...) //expanding each byte individually

offset += toRead
}

resp.Data = result
f.atime = time.Now()

f.atime = time.Now()

Expand All @@ -52,17 +75,39 @@ func (f *File) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.Wri
f.mu.Lock()
defer f.mu.Unlock()

end := req.Offset + int64(len(req.Data))
offset := req.Offset
data := req.Data
end := req.Offset + int64(len(req.Data)) // till where we'll be writing

// grow blocks if needed
for end > int64(len(f.blocks))*blockSize {
f.blocks = append(f.blocks, make([]byte, blockSize))
}

// Grow the buffer if needed
if end > int64(len(f.data)) {
newData := make([]byte, end)
copy(newData, f.data)
f.data = newData
// updating file size
if end > int64(f.size) {
f.size = uint64(end)
}

copy(f.data[req.Offset:], req.Data)
resp.Size = len(req.Data)
written := 0

for len(data) > 0 {
blockIndex := offset / blockSize
blockOffset := offset % blockSize

toWrite := min(int64(blockSize)-blockOffset, int64(len(data)))

n := copy(f.blocks[blockIndex][blockOffset:blockOffset+toWrite], data[:toWrite])

data = data[n:]
offset += int64(n)
written += n

}

resp.Size = written
f.mtime = time.Now()
f.ctime = time.Now() // writing to file constitutes changes in certain fields of inode too

f.mtime = time.Now()
f.ctime = time.Now() // writing to file constitutes changes in certain fields of inode too
Expand All @@ -80,17 +125,48 @@ func (f *File) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse
f.ctime = time.Now()
}

if req.Valid.Size() {
if req.Size < uint64(len(f.data)) {
f.data = f.data[:req.Size]
f.ctime = time.Now() // cuz creating file here
} else {
newData := make([]byte, req.Size)
copy(newData, f.data)
f.data = newData
if req.Valid.Uid() {

//if caller is not root and caller is trying to chown to uid that is not itself
if req.Header.Uid != 0 && req.Uid != req.Header.Uid{ // to get the uid of the process making the req -> checking the caller
Comment thread
Delta18-Git marked this conversation as resolved.
return syscall.EPERM
}

f.uid = req.Uid
f.ctime = time.Now()
}

if req.Valid.Gid() {
f.gid = req.Gid
f.ctime = time.Now()
}

if req.Valid.Size() { // mainly for truncate?

newSize := req.Size
needed := (newSize + uint64(blockSize) - 1) / uint64(blockSize) // ceil division to find how many blocks are required to store the new size

if newSize < f.size { //shrinking operation

//start from where we need to start
f.blocks = f.blocks[:needed]

if newSize > 0 {
last_offset := newSize % uint64(blockSize)
if last_offset != 0 {
clear(f.blocks[needed-1][last_offset:]) // since we are shirnking/truncating we need to remove(clear) the stuff we dont need
}
}
} else if newSize > f.size { // explanding operation
for uint64(len(f.blocks)) < needed {
f.blocks = append(f.blocks, make([]byte, blockSize))
}
}

f.size = newSize
f.mtime = time.Now()
f.ctime = time.Now()

}

if req.Valid.Atime() {
Expand All @@ -102,10 +178,12 @@ func (f *File) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse

resp.Attr.Inode = f.inode
resp.Attr.Mode = os.FileMode(f.mode)
resp.Attr.Size = uint64(len(f.data))
resp.Attr.Size = f.size
resp.Attr.Atime = f.atime
resp.Attr.Mtime = f.mtime
resp.Attr.Ctime = f.ctime
resp.Attr.Uid = f.uid
resp.Attr.Gid = f.gid

return nil
}
Expand Down
50 changes: 35 additions & 15 deletions internal/fs/fs.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fs

import (
"os"
"sync"
"sync/atomic"
"time"
Expand All @@ -19,37 +20,54 @@ func nextInode() uint64 {
}

func (f *FS) Root() (fs.Node, error) {

content := []byte("Hello from radFS!\n")
blocks := [][]byte{}
for i := 0; i < len(content); i += blockSize {
end := min(i+blockSize, len(content))
block := make([]byte, blockSize)
copy(block, content[i:end])
blocks = append(blocks, block)
}

root := &Dir{
inode: 1,
Nodes: map[string]fs.Node{

"hello.txt": &File{
inode: nextInode(),
data: []byte("Hello from radFS!\n"),
mode: 0o666,
atime: time.Now(),
mtime: time.Now(),
ctime: time.Now(),
inode: nextInode(),
blocks: blocks,
size: uint64(len(content)),
mode: 0o666,
atime: time.Now(),
mtime: time.Now(),
ctime: time.Now(),
uid: uint32(os.Getuid()),
gid: uint32(os.Getgid()),
},
},
fs: f,
atime: time.Now(),
mtime: time.Now(),
ctime: time.Now(),
uid: uint32(os.Getuid()),
gid: uint32(os.Getgid()),
}

return root, nil
}

type File struct {
mu sync.Mutex
inode uint64
data []byte
mode uint32
atime time.Time // read
mtime time.Time // write | truncate
ctime time.Time // metadata (setattr)
uid uint32
gid uint32
mu sync.Mutex
inode uint64
blocks [][]byte
size uint64
mode uint32
atime time.Time // read
mtime time.Time // write | truncate
ctime time.Time // metadata (setattr)
uid uint32
gid uint32
}

type Dir struct {
Expand All @@ -60,4 +78,6 @@ type Dir struct {
atime time.Time
mtime time.Time
ctime time.Time
uid uint32
gid uint32
}