Skip to content

Commit

Permalink
Symlinks support !
Browse files Browse the repository at this point in the history
  • Loading branch information
Unbewohnte committed Jan 22, 2022
1 parent 700811b commit 8eb2f18
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 40 deletions.
75 changes: 66 additions & 9 deletions src/fsys/dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type Directory struct {
Path string
Size uint64
RelativeParentPath string // Relative path to the directory, where the highest point in the hierarchy is the upmost parent dir. Set manually
Symlinks []*Symlink
Files []*File
Directories []*Directory
}
Expand Down Expand Up @@ -70,6 +71,7 @@ func GetDir(path string, recursive bool) (*Directory, error) {

var innerDirs []*Directory
var innerFiles []*File
var innerSymlinks []*Symlink
for _, entry := range entries {
entryInfo, err := entry.Info()
if err != nil {
Expand All @@ -93,22 +95,42 @@ func GetDir(path string, recursive bool) (*Directory, error) {
// if not - skip the directory and only work with the files

} else {
innerFilePath := filepath.Join(absPath, entryInfo.Name())
// not a directory

innerFile, err := GetFile(innerFilePath)
if err != nil {
// skip this file
continue
}
switch entryInfo.Mode()&os.ModeSymlink != 0 {
case true:
// it is a symlink
innerSymlinkPath := filepath.Join(absPath, entryInfo.Name())

symlink, err := GetSymlink(innerSymlinkPath, false)
if err != nil {
// skip this symlink
continue
}

innerSymlinks = append(innerSymlinks, symlink)

directory.Size += innerFile.Size
case false:
// it is a usual file

innerFilePath := filepath.Join(absPath, entryInfo.Name())

innerFile, err := GetFile(innerFilePath)
if err != nil {
// skip this file
continue
}

innerFiles = append(innerFiles, innerFile)
directory.Size += innerFile.Size

innerFiles = append(innerFiles, innerFile)
}
}
}

directory.Directories = innerDirs
directory.Files = innerFiles
directory.Symlinks = innerSymlinks

return &directory, nil
}
Expand All @@ -134,7 +156,27 @@ func (dir *Directory) GetAllFiles(recursive bool) []*File {
return files
}

// Sets `RelativeParentPath` relative to the given base path.
// Returns every symlink in that directory
func (dir *Directory) GetAllSymlinks(recursive bool) []*Symlink {
var symlinks []*Symlink = dir.Symlinks

if recursive {
if len(dir.Directories) == 0 {
return symlinks
}

for _, innerDir := range dir.Directories {
innerSymlinks := innerDir.GetAllSymlinks(recursive)
symlinks = append(symlinks, innerSymlinks...)
}
} else {
symlinks = dir.Symlinks
}

return symlinks
}

// Sets `RelativeParentPath` relative to the given base path for files and `Path`, `TargetPath` for symlinks so the
// file with such path:
// /home/user/directory/somefile.txt
// had a relative path like that:
Expand All @@ -150,5 +192,20 @@ func (dir *Directory) SetRelativePaths(base string, recursive bool) error {
file.RelativeParentPath = relPath

}

for _, symlink := range dir.GetAllSymlinks(recursive) {
symRelPath, err := filepath.Rel(base, symlink.Path)
if err != nil {
return err
}
symlink.Path = symRelPath

symRelTargetPath, err := filepath.Rel(base, symlink.TargetPath)
if err != nil {
return err
}
symlink.TargetPath = symRelTargetPath
}

return nil
}
36 changes: 18 additions & 18 deletions src/fsys/dir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
package fsys

import (
"os"
"path/filepath"
"testing"
)

Expand Down Expand Up @@ -79,25 +81,23 @@ func Test_GetFiles(t *testing.T) {

}

// func Test_SetRelativePaths(t *testing.T) {
// dirpath := "../testfiles/"
func Test_GetSymlinks(t *testing.T) {
dirpath := "../testfiles/"

// dir, err := GetDir(dirpath, true)
// if err != nil {
// t.Fatalf("%s", err)
// }
os.Symlink(filepath.Join(dirpath, "testfile.txt"), filepath.Join(dirpath, "testsymlink.txt"))
os.Symlink(filepath.Join(dirpath, "testdir", "testfile2.txt"), filepath.Join(dirpath, "testdir", "testsymlink2.txt"))

// absDirPath, err := filepath.Abs(dirpath)
// if err != nil {
// t.Fatalf("%s", err)
// }
dir, err := GetDir(dirpath, true)
if err != nil {
t.Fatalf("%s", err)
}

// err = dir.SetRelativePaths(absDirPath, true)
// if err != nil {
// t.Fatalf("%s", err)
// }
// recursive
symlinks := dir.GetAllSymlinks(true)

symlinkCount := 2

// for count, file := range dir.GetAllFiles(true) {
// t.Errorf("[%d] %v\n", count, file.RelativeParentPath)
// }
// }
if len(symlinks) != symlinkCount {
t.Fatalf("expected to get %d symlinks; got %d\n", symlinkCount, len(symlinks))
}
}
70 changes: 70 additions & 0 deletions src/fsys/symlink.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
This file is a part of ftu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package fsys

import (
"fmt"
"os"
)

type Symlink struct {
TargetPath string
Path string
}

// Checks whether path is referring to a symlink or not
func IsSymlink(path string) (bool, error) {
stats, err := os.Lstat(path)
if err != nil {
return false, err
}
isSymlink := stats.Mode()&os.ModeSymlink != 0

return isSymlink, nil
}

var ErrorNotSymlink error = fmt.Errorf("not a symlink")

// get necessary information about a symlink in a filesystem. If check is false -
// does not check if path REALLY refers to a symlink
func GetSymlink(path string, check bool) (*Symlink, error) {
if check {
isSymlink, err := IsSymlink(path)
if err != nil {
return nil, err
}
if !isSymlink {
return nil, ErrorNotSymlink
}
}

target, err := os.Readlink(path)
if err != nil {
return nil, err
}

symlink := Symlink{
TargetPath: target,
Path: path,
}

return &symlink, nil
}
22 changes: 22 additions & 0 deletions src/fsys/symlink_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package fsys

import (
"os"
"path/filepath"
"testing"
)

func Test_IsSymlink(t *testing.T) {
dirpath := "../testfiles/"

symlinkPath := filepath.Join(dirpath, "testsymlink.txt")
os.Symlink(filepath.Join(dirpath, "testfile.txt"), symlinkPath)

isSymlink, err := IsSymlink(symlinkPath)
if err != nil {
t.Fatalf("%s\n", err)
}
if !isSymlink {
t.Fatalf("%s expected to be a symlink\n", symlinkPath)
}
}
2 changes: 1 addition & 1 deletion src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
)

var (
VERSION string = "v2.2.3"
VERSION string = "v2.3.0"

versionInformation string = fmt.Sprintf("ftu %s\nfile transferring utility\n\nCopyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte ([email protected]))\nThis program comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome to redistribute it under certain conditions; type \"ftu -l\" for details.\n", VERSION)

Expand Down
70 changes: 58 additions & 12 deletions src/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,18 @@ type netInfo struct {

// Sending-side node information
type sending struct {
ServingPath string // path to the thing that will be sent
IsDirectory bool // is ServingPath a directory
Recursive bool // recursively send directory
CanSendBytes bool // is the other node ready to receive another piece
AllowedToTransfer bool // the way to notify the mainloop of a sending node to start sending pieces of files
InTransfer bool // already transferring|receiving files
FilesToSend []*fsys.File
CurrentFileID uint64 // an id of a file that is currently being transported
SentBytes uint64 // how many bytes sent already
TotalTransferSize uint64 // how many bytes will be sent in total
ServingPath string // path to the thing that will be sent
IsDirectory bool // is ServingPath a directory
Recursive bool // recursively send directory
CanSendBytes bool // is the other node ready to receive another piece
AllowedToTransfer bool // the way to notify the mainloop of a sending node to start sending pieces of files
InTransfer bool // already transferring|receiving files
FilesToSend []*fsys.File
SymlinksToSend []*fsys.Symlink
CurrentFileID uint64 // an id of a file that is currently being transported
SentBytes uint64 // how many bytes sent already
TotalTransferSize uint64 // how many bytes will be sent in total
CurrentSymlinkIndex uint64 // current index of a symlink that is
}

// Receiving-side node information
Expand Down Expand Up @@ -355,8 +357,10 @@ func (node *Node) send() {
panic(err)
}
filesToSend := DIRTOSEND.GetAllFiles(node.transferInfo.Sending.Recursive)
symlinksToSend := DIRTOSEND.GetAllSymlinks(node.transferInfo.Sending.Recursive)

node.transferInfo.Sending.SymlinksToSend = symlinksToSend

// notify the other node about all the files that are going to be sent
for counter, file := range filesToSend {
// assign ID and add it to the node sendlist
file.ID = uint64(counter)
Expand Down Expand Up @@ -410,7 +414,14 @@ func (node *Node) send() {

// Transfer section

if len(node.transferInfo.Sending.FilesToSend) == 0 {
// if all files have been sent -> send symlinks
if len(node.transferInfo.Sending.FilesToSend) == 0 && node.transferInfo.Sending.CurrentSymlinkIndex < uint64(len(node.transferInfo.Sending.SymlinksToSend)) {
protocol.SendSymlink(node.transferInfo.Sending.SymlinksToSend[node.transferInfo.Sending.CurrentSymlinkIndex], node.netInfo.Conn, encrKey)
node.transferInfo.Sending.CurrentSymlinkIndex++
continue
}

if len(node.transferInfo.Sending.FilesToSend) == 0 && node.transferInfo.Sending.CurrentSymlinkIndex == uint64(len(node.transferInfo.Sending.SymlinksToSend)) {
// if there`s nothing else to send - create and send DONE packet
protocol.SendPacket(node.netInfo.Conn, protocol.Packet{
Header: protocol.HeaderDone,
Expand Down Expand Up @@ -857,6 +868,41 @@ func (node *Node) receive() {

fmt.Printf("\nGot an encryption key: %s", encrKey)

case protocol.HeaderSymlink:
// SYMLINK~(string size in binary)(location in the filesystem)(string size in binary)(location of a target)
packetReader := bytes.NewReader(incomingPacket.Body)

// extract the location of the symlink
var locationSize uint64
binary.Read(packetReader, binary.BigEndian, &locationSize)

symlinkLocationBytes := make([]byte, locationSize)
packetReader.Read(symlinkLocationBytes)

// extract the target of a symlink
var targetSize uint64
binary.Read(packetReader, binary.BigEndian, &targetSize)

symlinkTargetLocationBytes := make([]byte, targetSize)
packetReader.Read(symlinkTargetLocationBytes)

symlinkLocation := string(symlinkLocationBytes)
symlinkTargetLocation := string(symlinkTargetLocationBytes)

// create a symlink

// should be already downloaded
symlinkDir := filepath.Join(node.transferInfo.Receiving.DownloadsPath, filepath.Dir(symlinkLocation))
os.MkdirAll(symlinkDir, os.ModePerm)

os.Symlink(
filepath.Join(node.transferInfo.Receiving.DownloadsPath, symlinkTargetLocation),
filepath.Join(node.transferInfo.Receiving.DownloadsPath, symlinkLocation))

protocol.SendPacket(node.netInfo.Conn, protocol.Packet{
Header: protocol.HeaderReady,
})

case protocol.HeaderDone:
node.mutex.Lock()
node.stopped = true
Expand Down
7 changes: 7 additions & 0 deletions src/protocol/headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,10 @@ const HeaderDirectory Header = "DIRECTORY"
// Body must contain a file ID.
// ie: ALREADYHAVE~(file ID in binary)
const HeaderAlreadyHave Header = "ALREADYHAVE"

// SYMLINK
// Sent by sender AFTER ALL FILES has been sent already. Indicates that there
// is a symlink in some place that points to some other already received file.
// Body must contain information where the symlink is and the target file.
// ie: SYMLINK~(string size in binary)(location in the filesystem)(string size in binary)(location of a target)
const HeaderSymlink Header = "SYMLINK"
Loading

0 comments on commit 8eb2f18

Please sign in to comment.