Skip to content

Commit

Permalink
Added File.Stat and example
Browse files Browse the repository at this point in the history
  • Loading branch information
davecheney committed Nov 6, 2013
1 parent d533956 commit e7b8e37
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 7 deletions.
81 changes: 76 additions & 5 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,18 +317,43 @@ func (c *Client) Lstat(path string) (os.FileInfo, error) {
// File represents a remote file.
type File struct {
c *Client
path string
handle string
offset uint64 // current offset within remote file
}

func (f *File) Read(buf []byte) (int, error) {
n, err := f.c.readAt(f.handle, f.offset, buf)
// Close closes the File, rendering it unusable for I/O. It returns an
// error, if any.
func (f *File) Close() error {
return f.c.close(f.handle)
}

// Read reads up to len(b) bytes from the File. It returns the number of
// bytes read and an error, if any. EOF is signaled by a zero count with
// err set to io.EOF.
func (f *File) Read(b []byte) (int, error) {
n, err := f.c.readAt(f.handle, f.offset, b)
f.offset += uint64(n)
return int(n), err
}

func (f *File) Close() error {
return f.c.close(f.handle)
// ReadAt reads len(b) bytes from the File starting at byte offset off. It
// returns the number of bytes read and the error, if any. ReadAt always
// returns a non-nil error when n < len(b). At end of file, that error is
// io.EOF.
func (f *File) ReadAt(b []byte, off int64) (int, error) {
n, err := f.c.readAt(f.handle, uint64(off), b)
return int(n), err
}

// Stat returns the FileInfo structure describing file. If there is an
// error, it will be of type *PathError.
func (f *File) Stat() (os.FileInfo, error) {
fi, err := f.c.fstat(f.handle)
if err == nil {
fi.name = f.path
}
return fi, err
}

// Open opens the named file for reading. If successful, methods on the
Expand Down Expand Up @@ -365,7 +390,7 @@ func (c *Client) Open(path string) (*File, error) {
return nil, &unexpectedIdErr{id, sid}
}
handle, _ := unmarshalString(data)
return &File{c: c, handle: handle}, nil
return &File{c: c, path: path, handle: handle}, nil
case SSH_FXP_STATUS:
sid, data := unmarshalUint32(data)
if sid != id {
Expand Down Expand Up @@ -481,3 +506,49 @@ func (c *Client) close(handle string) error {
return unimplementedPacketErr(typ)
}
}

func (c *Client) fstat(handle string) (*attr, error) {
type packet struct {
Type byte
Id uint32
Handle string
}
c.mu.Lock()
defer c.mu.Unlock()
id := c.nextId()
if err := sendPacket(c.w, packet{
Type: SSH_FXP_FSTAT,
Id: id,
Handle: handle,
}); err != nil {
return nil, err
}
typ, data, err := recvPacket(c.r)
if err != nil {
return nil, err
}
switch typ {
case SSH_FXP_ATTRS:
sid, data := unmarshalUint32(data)
if sid != id {
return nil, &unexpectedIdErr{id, sid}
}
attr, _ := unmarshalAttrs(data)
return attr, nil
case SSH_FXP_STATUS:
sid, data := unmarshalUint32(data)
if sid != id {
return nil, &unexpectedIdErr{id, sid}
}
code, data := unmarshalUint32(data)
msg, data := unmarshalString(data)
lang, _ := unmarshalString(data)
return nil, &StatusError{
Code: code,
msg: msg,
lang: lang,
}
default:
return nil, unimplementedPacketErr(typ)
}
}
18 changes: 16 additions & 2 deletions examples/gsftp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func main() {
switch cmd := flag.Args()[0]; cmd {
case "ls":
if len(flag.Args()) < 2 {
log.Fatalf("%s ls: remote path required", os.Args[0])
log.Fatalf("%s %s: remote path required", cmd, os.Args[0])
}
walker := client.Walk(flag.Args()[1])
for walker.Step() {
Expand All @@ -67,7 +67,7 @@ func main() {
}
case "fetch":
if len(flag.Args()) < 2 {
log.Fatalf("%s ls: remote path required", os.Args[0])
log.Fatalf("%s %s: remote path required", cmd, os.Args[0])
}
f, err := client.Open(flag.Args()[1])
if err != nil {
Expand All @@ -77,6 +77,20 @@ func main() {
if _, err := io.Copy(os.Stdout, f); err != nil {
log.Fatal(err)
}
case "stat":
if len(flag.Args()) < 2 {
log.Fatalf("%s %s: remote path required", cmd, os.Args[0])
}
f, err := client.Open(flag.Args()[1])
if err != nil {
log.Fatal(err)
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
log.Fatal("unable to stat file: %v", err)
}
fmt.Printf("%s %d %v\n", fi.Name(), fi.Size(), fi.Mode())
default:
log.Fatal("unknown subcommand: %v", cmd)
}
Expand Down

0 comments on commit e7b8e37

Please sign in to comment.