-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add first start of an rsync receiver
This receiver is able to list files, and correctly receive them (matching checksum), but the receiver does not write any files to disk yet: % gokr-rsync -a \ rsync://rsync.chiark.greenend.org.uk/ftp/users/sgtatham/putty-website-mirror/ Now that we have a sender and a receiver (in a client and a server, respectively, roles cannot be reversed currently), we can split out common data types, constants (package rsync) and wire-level routines (package rsyncwire). The terminology matches tridge rsync (file names, function names, data types) where possible.
- Loading branch information
1 parent
f470592
commit d5a5b3d
Showing
22 changed files
with
2,287 additions
and
1,316 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Tool gokr-rsync is an rsync receiver Go implementation. | ||
package main | ||
|
||
import ( | ||
"log" | ||
"os" | ||
|
||
"github.com/gokrazy/rsync/internal/receivermaincmd" | ||
) | ||
|
||
func main() { | ||
if err := receivermaincmd.Main(os.Args, os.Stdin, os.Stdout, os.Stderr); err != nil { | ||
log.Fatal(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package rsync | ||
|
||
// rsync.h | ||
const ( | ||
XMIT_TOP_DIR = (1 << 0) | ||
XMIT_SAME_MODE = (1 << 1) | ||
XMIT_EXTENDED_FLAGS = (1 << 2) | ||
XMIT_SAME_RDEV_pre28 = XMIT_EXTENDED_FLAGS /* Only in protocols < 28 */ | ||
XMIT_SAME_UID = (1 << 3) | ||
XMIT_SAME_GID = (1 << 4) | ||
XMIT_SAME_NAME = (1 << 5) | ||
XMIT_LONG_NAME = (1 << 6) | ||
XMIT_SAME_TIME = (1 << 7) | ||
XMIT_SAME_RDEV_MAJOR = (1 << 8) | ||
XMIT_HAS_IDEV_DATA = (1 << 9) | ||
XMIT_SAME_DEV = (1 << 10) | ||
XMIT_RDEV_MINOR_IS_SMALL = (1 << 11) | ||
) | ||
|
||
// as per /usr/include/bits/stat.h: | ||
const ( | ||
S_IFMT = 0o0170000 // bits determining the file type | ||
S_IFDIR = 0o0040000 // Directory | ||
S_IFCHR = 0o0020000 // Character device | ||
S_IFBLK = 0o0060000 // Block device | ||
S_IFREG = 0o0100000 // Regular file | ||
S_IFIFO = 0o0010000 // FIFO | ||
S_IFLNK = 0o0120000 // Symbolic link | ||
S_IFSOCK = 0o0140000 // Socket | ||
) | ||
|
||
// ProtocolVersion defines the currently implemented rsync protocol | ||
// version. Protocol version 27 seems to be the safest bet for wide | ||
// compatibility: version 27 was introduced by rsync 2.6.0 (released 2004), and | ||
// is supported by openrsync and rsyn. | ||
const ProtocolVersion = 27 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package receivermaincmd | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"log" | ||
"net" | ||
"net/url" | ||
"os" | ||
"strings" | ||
|
||
"github.com/gokrazy/rsync" | ||
) | ||
|
||
// rsync/clientserver.c:start_socket_client | ||
func socketClient(osenv osenv, opts *Opts, src, dest string) error { | ||
u, err := url.Parse(src) | ||
if err != nil { | ||
return err | ||
} | ||
host := u.Host | ||
if _, _, err := net.SplitHostPort(host); err != nil { | ||
host += ":873" // rsync daemon port | ||
} | ||
log.Printf("Opening TCP connection to %s", host) | ||
conn, err := net.Dial("tcp", host) | ||
if err != nil { | ||
return err | ||
} | ||
path := strings.TrimPrefix(u.Path, "/") | ||
if path == "" { | ||
return fmt.Errorf("empty remote path") | ||
} | ||
module := path | ||
if idx := strings.IndexByte(module, '/'); idx > -1 { | ||
module = module[:idx] | ||
} | ||
log.Printf("rsync module %q, path %q", module, path) | ||
if err := startInbandExchange(opts, conn, module, path); err != nil { | ||
return err | ||
} | ||
if err := clientRun(osenv, opts, conn, dest); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
// rsync/clientserver.c:start_inband_exchange | ||
func startInbandExchange(opts *Opts, conn io.ReadWriter, module, path string) error { | ||
rd := bufio.NewReader(conn) | ||
|
||
// send client greeting | ||
fmt.Fprintf(conn, "@RSYNCD: %d\n", rsync.ProtocolVersion) | ||
|
||
// read server greeting | ||
serverGreeting, err := rd.ReadString('\n') | ||
if err != nil { | ||
return fmt.Errorf("ReadString: %v", err) | ||
} | ||
serverGreeting = strings.TrimSpace(serverGreeting) | ||
const serverGreetingPrefix = "@RSYNCD: " | ||
if !strings.HasPrefix(serverGreeting, serverGreetingPrefix) { | ||
return fmt.Errorf("invalid server greeting: got %q", serverGreeting) | ||
} | ||
// protocol negotiation: require at least version 27 | ||
serverGreeting = strings.TrimPrefix(serverGreeting, serverGreetingPrefix) | ||
var remoteProtocol, remoteSub int32 | ||
if _, err := fmt.Sscanf(serverGreeting, "%d.%d", &remoteProtocol, &remoteSub); err != nil { | ||
if _, err := fmt.Sscanf(serverGreeting, "%d", &remoteProtocol); err != nil { | ||
return fmt.Errorf("reading server greeting: %v", err) | ||
} | ||
} | ||
if remoteProtocol < 27 { | ||
return fmt.Errorf("server version %d too old", remoteProtocol) | ||
} | ||
|
||
log.Printf("(Client) Protocol versions: remote=%d, negotiated=%d", remoteProtocol, rsync.ProtocolVersion) | ||
log.Printf("Client checksum: md4") | ||
|
||
// send module name | ||
fmt.Fprintf(conn, "%s\n", module) | ||
for { | ||
line, err := rd.ReadString('\n') | ||
if err != nil { | ||
return fmt.Errorf("did not get server startup line: %v", err) | ||
} | ||
line = strings.TrimSpace(line) | ||
|
||
if strings.HasPrefix(line, "@RSYNCD: AUTHREQD ") { | ||
// TODO: implement support for authentication | ||
return fmt.Errorf("authentication not yet implemented") | ||
} | ||
|
||
if line == "@RSYNCD: OK" { | ||
break | ||
} | ||
|
||
// TODO: @RSYNCD: EXIT after listing modules | ||
|
||
if strings.HasPrefix(line, "@ERROR") { | ||
fmt.Fprintf(os.Stderr, "%s\n", line) | ||
return fmt.Errorf("abort (rsync fatal error)") | ||
} | ||
|
||
// print rsync server message of the day (MOTD) | ||
fmt.Fprintf(os.Stdout, "%s\n", line) | ||
} | ||
|
||
sargv := serverOptions(opts) | ||
sargv = append(sargv, ".") | ||
if path != "" { | ||
sargv = append(sargv, path) | ||
} | ||
log.Printf("sending daemon args: %s", sargv) | ||
for _, argv := range sargv { | ||
fmt.Fprintf(conn, "%s\n", argv) | ||
} | ||
fmt.Fprintf(conn, "\n") | ||
|
||
return nil | ||
} |
Oops, something went wrong.