Files
httprunner/hrp/pkg/gidevice/afc.go
2022-10-23 22:59:34 +08:00

553 lines
15 KiB
Go

package gidevice
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"path"
"strconv"
"time"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
var ErrAfcStatNotExist = errors.New("afc stat: no such file or directory")
var _ Afc = (*afc)(nil)
func newAfc(client *libimobiledevice.AfcClient) *afc {
return &afc{client: client}
}
type afc struct {
client *libimobiledevice.AfcClient
}
func (c *afc) DiskInfo() (info *AfcDiskInfo, err error) {
if err = c.client.Send(libimobiledevice.AfcOperationGetDeviceInfo, nil, nil); err != nil {
return nil, fmt.Errorf("afc send 'DiskInfo': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return nil, fmt.Errorf("afc receive 'DiskInfo': %w", err)
}
m := respMsg.Map()
info = &AfcDiskInfo{
Model: m["Model"],
}
if info.TotalBytes, err = strconv.ParseUint(m["FSTotalBytes"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'DiskInfo': %w", err)
}
if info.FreeBytes, err = strconv.ParseUint(m["FSFreeBytes"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'DiskInfo': %w", err)
}
if info.BlockSize, err = strconv.ParseUint(m["FSBlockSize"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'DiskInfo': %w", err)
}
return
}
func (c *afc) ReadDir(dirname string) (names []string, err error) {
if err = c.client.Send(libimobiledevice.AfcOperationReadDir, toCString(dirname), nil); err != nil {
return nil, fmt.Errorf("afc send 'ReadDir': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return nil, fmt.Errorf("afc receive 'ReadDir': %w", err)
}
if err = respMsg.Err(); err != nil {
return nil, fmt.Errorf("afc 'ReadDir': %w", err)
}
names = respMsg.Strings()
return
}
func (c *afc) Stat(filename string) (info *AfcFileInfo, err error) {
if err = c.client.Send(libimobiledevice.AfcOperationGetFileInfo, toCString(filename), nil); err != nil {
return nil, fmt.Errorf("afc send 'Stat': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return nil, fmt.Errorf("afc receive 'Stat': %w", err)
}
m := respMsg.Map()
if len(m) == 0 {
return nil, ErrAfcStatNotExist
}
info = &AfcFileInfo{
source: m,
name: path.Base(filename),
ifmt: m["st_ifmt"],
}
if info.creationTime, err = strconv.ParseUint(m["st_birthtime"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'Stat': %w", err)
}
if info.blocks, err = strconv.ParseUint(m["st_blocks"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'Stat': %w", err)
}
if info.modTime, err = strconv.ParseUint(m["st_mtime"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'Stat': %w", err)
}
if info.nlink, err = strconv.ParseUint(m["st_nlink"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'Stat': %w", err)
}
if info.size, err = strconv.ParseUint(m["st_size"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'Stat': %w", err)
}
return
}
func (c *afc) Open(filename string, mode AfcFileMode) (file *AfcFile, err error) {
buf := new(bytes.Buffer)
if err = binary.Write(buf, binary.LittleEndian, uint64(mode)); err != nil {
return nil, fmt.Errorf("afc send 'Open': %w", err)
}
buf.Write(toCString(filename))
if err = c.client.Send(libimobiledevice.AfcOperationFileOpen, buf.Bytes(), nil); err != nil {
return nil, fmt.Errorf("afc send 'Open': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return nil, fmt.Errorf("afc receive 'Open': %w", err)
}
if err = respMsg.Err(); err != nil {
return nil, fmt.Errorf("afc 'Open': %w", err)
}
if respMsg.Operation != libimobiledevice.AfcOperationFileOpenResult {
return nil, fmt.Errorf("afc operation mistake 'Open': '%d'", respMsg.Operation)
}
file = &AfcFile{
client: c.client,
fd: respMsg.Uint64(),
}
return
}
func (c *afc) Remove(filePath string) (err error) {
if err = c.client.Send(libimobiledevice.AfcOperationRemovePath, toCString(filePath), nil); err != nil {
return fmt.Errorf("afc send 'Remove': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'Remove': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'Remove': %w", err)
}
return
}
func (c *afc) Rename(oldPath string, newPath string) (err error) {
if err = c.client.Send(libimobiledevice.AfcOperationRenamePath, toCString(oldPath, newPath), nil); err != nil {
return fmt.Errorf("afc send 'Rename': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'Rename': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'Rename': %w", err)
}
return
}
func (c *afc) Mkdir(path string) (err error) {
if err = c.client.Send(libimobiledevice.AfcOperationMakeDir, toCString(path), nil); err != nil {
return fmt.Errorf("afc send 'Mkdir': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'Mkdir': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'Mkdir': %w", err)
}
return
}
func (c *afc) Link(oldName string, newName string, linkType AfcLinkType) (err error) {
buf := new(bytes.Buffer)
_ = binary.Write(buf, binary.LittleEndian, uint64(linkType))
buf.Write(toCString(oldName, newName))
if err = c.client.Send(libimobiledevice.AfcOperationMakeLink, buf.Bytes(), nil); err != nil {
return fmt.Errorf("afc send 'Link': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'Link': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'Link': %w", err)
}
return
}
func (c *afc) Truncate(filePath string, size int64) (err error) {
buf := new(bytes.Buffer)
_ = binary.Write(buf, binary.LittleEndian, uint64(size))
buf.Write(toCString(filePath))
if err = c.client.Send(libimobiledevice.AfcOperationTruncateFile, buf.Bytes(), nil); err != nil {
return fmt.Errorf("afc send 'Truncate': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'Truncate': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'Truncate': %w", err)
}
return
}
func (c *afc) SetFileModTime(filePath string, modTime time.Time) (err error) {
buf := new(bytes.Buffer)
_ = binary.Write(buf, binary.LittleEndian, uint64(modTime.Unix()))
buf.Write(toCString(filePath))
if err = c.client.Send(libimobiledevice.AfcOperationSetFileModTime, buf.Bytes(), nil); err != nil {
return fmt.Errorf("afc send 'SetFileModTime': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'SetFileModTime': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'SetFileModTime': %w", err)
}
return
}
func (c *afc) Hash(filePath string) ([]byte, error) {
var err error
if err = c.client.Send(libimobiledevice.AfcOperationGetFileHash, toCString(filePath), nil); err != nil {
return nil, fmt.Errorf("afc send 'Hash': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return nil, fmt.Errorf("afc receive 'Hash': %w", err)
}
if err = respMsg.Err(); err != nil {
return nil, fmt.Errorf("afc 'Hash': %w", err)
}
return respMsg.Payload, nil
}
func (c *afc) HashWithRange(filePath string, start, end uint64) ([]byte, error) {
buf := new(bytes.Buffer)
_ = binary.Write(buf, binary.LittleEndian, start)
_ = binary.Write(buf, binary.LittleEndian, end)
buf.Write(toCString(filePath))
var err error
if err = c.client.Send(libimobiledevice.AfcOperationGetFileHashRange, buf.Bytes(), nil); err != nil {
return nil, fmt.Errorf("afc send 'HashWithRange': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return nil, fmt.Errorf("afc receive 'HashWithRange': %w", err)
}
if err = respMsg.Err(); err != nil {
return nil, fmt.Errorf("afc 'HashWithRange': %w", err)
}
return respMsg.Payload, nil
}
// RemoveAll since iOS6+
func (c *afc) RemoveAll(path string) (err error) {
if err = c.client.Send(libimobiledevice.AfcOperationRemovePathAndContents, toCString(path), nil); err != nil {
return fmt.Errorf("afc send 'RemoveAll': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'RemoveAll': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'RemoveAll': %w", err)
}
return
}
func (c *afc) WriteFile(filename string, data []byte, perm AfcFileMode) (err error) {
var file *AfcFile
if file, err = c.Open(filename, perm); err != nil {
return err
}
defer func() {
err = file.Close()
}()
if _, err = file.Write(data); err != nil {
return err
}
return
}
func toCString(s ...string) []byte {
buf := new(bytes.Buffer)
for _, v := range s {
buf.WriteString(v)
buf.WriteByte(0)
}
return buf.Bytes()
}
type AfcDiskInfo struct {
Model string
TotalBytes uint64
FreeBytes uint64
BlockSize uint64
}
type AfcFileInfo struct {
name string
creationTime uint64
blocks uint64
ifmt string
modTime uint64
nlink uint64
size uint64
source map[string]string
}
func (f *AfcFileInfo) Name() string {
return f.name
}
func (f *AfcFileInfo) Size() int64 {
return int64(f.size)
}
// func (f *AfcFileInfo) Mode() os.FileMode {
// return os.ModeType
// }
func (f *AfcFileInfo) ModTime() time.Time {
return time.Unix(0, int64(f.modTime))
}
func (f *AfcFileInfo) IsDir() bool {
return f.ifmt == "S_IFDIR"
}
// func (f *AfcFileInfo) Sys() interface{} {
// return f.source
// }
func (f *AfcFileInfo) CreationTime() time.Time {
return time.Unix(0, int64(f.creationTime))
}
// func (f *AfcFileInfo) Blocks() uint64 {
// return f.blocks
// }
// func (f *AfcFileInfo) Format() string {
// return f.ifmt
// }
// func (f *AfcFileInfo) Link() uint64 {
// return f.nlink
// }
// func (f *AfcFileInfo) PhysicalSize(info *AfcDiskInfo) int64 {
// return int64(f.blocks * (info.BlockSize / 8))
// }
type AfcFileMode uint32
const (
AfcFileModeRdOnly AfcFileMode = 0x00000001
AfcFileModeRw AfcFileMode = 0x00000002
AfcFileModeWrOnly AfcFileMode = 0x00000003
AfcFileModeWr AfcFileMode = 0x00000004
AfcFileModeAppend AfcFileMode = 0x00000005
AfcFileModeRdAppend AfcFileMode = 0x00000006
)
type AfcLockType int
const (
AfcLockTypeSharedLock AfcLockType = 1 | 4
AfcLockTypeExclusiveLock AfcLockType = 2 | 4
AfcLockTypeUnlock AfcLockType = 8 | 4
)
type AfcFile struct {
client *libimobiledevice.AfcClient
fd uint64
reader *bytes.Reader
}
func (f *AfcFile) op(o ...uint64) []byte {
buf := new(bytes.Buffer)
_ = binary.Write(buf, binary.LittleEndian, f.fd)
if len(o) == 0 {
return buf.Bytes()
}
for _, v := range o {
_ = binary.Write(buf, binary.LittleEndian, v)
}
return buf.Bytes()
}
func (f *AfcFile) Lock(lockType AfcLockType) (err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileRefLock, f.op(uint64(lockType)), nil); err != nil {
return fmt.Errorf("afc file send 'Lock': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = f.client.Receive(); err != nil {
return fmt.Errorf("afc file receive 'Lock': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc file 'Lock': %w", err)
}
return
}
func (f *AfcFile) Unlock() (err error) {
return f.Lock(AfcLockTypeUnlock)
}
func (f *AfcFile) Read(b []byte) (n int, err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileRead, f.op(uint64(len(b))), nil); err != nil {
return -1, fmt.Errorf("afc file send 'Read': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = f.client.Receive(); err != nil {
return -1, fmt.Errorf("afc file receive 'Read': %w", err)
}
if err = respMsg.Err(); err != nil {
return -1, fmt.Errorf("afc file 'Read': %w", err)
}
if respMsg.Payload == nil {
return 0, io.EOF
}
if f.reader == nil {
f.reader = bytes.NewReader(respMsg.Payload)
} else {
f.reader.Reset(respMsg.Payload)
}
return f.reader.Read(b)
}
func (f *AfcFile) Write(b []byte) (n int, err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileWrite, f.op(), b); err != nil {
return -1, fmt.Errorf("afc file send 'Write': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = f.client.Receive(); err != nil {
return -1, fmt.Errorf("afc file receive 'Write': %w", err)
}
if err = respMsg.Err(); err != nil {
return -1, fmt.Errorf("afc file 'Write': %w", err)
}
n = len(b)
return
}
func (f *AfcFile) Tell() (n uint64, err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileTell, f.op(), nil); err != nil {
return 0, fmt.Errorf("afc file 'Tell': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = f.client.Receive(); err != nil {
return 0, fmt.Errorf("afc file receive 'Tell': %w", err)
}
if err = respMsg.Err(); err != nil {
return 0, fmt.Errorf("afc file 'Tell': %w", err)
}
if respMsg.Operation != libimobiledevice.AfcOperationFileTellResult {
return 0, fmt.Errorf("afc operation mistake 'Tell': '%d'", respMsg.Operation)
}
n = respMsg.Uint64()
return
}
func (f *AfcFile) Seek(offset int64, whence int) (ret int64, err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileSeek, f.op(uint64(whence), uint64(offset)), nil); err != nil {
return -1, fmt.Errorf("afc file 'Seek': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = f.client.Receive(); err != nil {
return -1, fmt.Errorf("afc file receive 'Seek': %w", err)
}
if err = respMsg.Err(); err != nil {
return -1, fmt.Errorf("afc file 'Seek': %w", err)
}
var tell uint64
if tell, err = f.Tell(); err != nil {
return -1, err
}
ret = int64(tell)
return
}
func (f *AfcFile) Truncate(size int64) (err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileSetSize, f.op(uint64(size)), nil); err != nil {
return fmt.Errorf("afc file 'Truncate': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = f.client.Receive(); err != nil {
return fmt.Errorf("afc file receive 'Truncate': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc file 'Truncate': %w", err)
}
return
}
func (f *AfcFile) Close() (err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileClose, f.op(), nil); err != nil {
return fmt.Errorf("afc file 'Close': %w", err)
}
if _, err = f.client.Receive(); err != nil {
return fmt.Errorf("afc file receive 'Close': %w", err)
}
return
}
type AfcLinkType int
const (
AfcLinkTypeHardLink AfcLinkType = 1
AfcLinkTypeSymLink AfcLinkType = 2
)