2018-10-26 15:51:28 +02:00
package operations
import (
2019-06-17 10:34:30 +02:00
"context"
2023-05-24 11:51:30 +02:00
"errors"
2021-11-04 11:12:57 +01:00
"fmt"
2020-06-12 08:04:30 +02:00
"io"
"mime"
"mime/multipart"
"net/http"
2020-08-10 18:09:46 +02:00
"path"
2018-10-26 15:51:28 +02:00
"strings"
2020-06-12 08:04:30 +02:00
"time"
2018-10-26 15:51:28 +02:00
2019-07-28 19:47:38 +02:00
"github.com/rclone/rclone/fs"
2023-09-05 11:54:01 +02:00
"github.com/rclone/rclone/fs/config"
2023-05-24 11:51:30 +02:00
"github.com/rclone/rclone/fs/hash"
2019-07-28 19:47:38 +02:00
"github.com/rclone/rclone/fs/rc"
2023-09-05 11:54:01 +02:00
"github.com/rclone/rclone/lib/diskusage"
2018-10-26 15:51:28 +02:00
)
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/list" ,
AuthRequired : true ,
Fn : rcList ,
Title : "List the given remote and path in JSON format" ,
2021-08-16 11:30:01 +02:00
Help : ` This takes the following parameters :
2018-10-26 15:51:28 +02:00
2020-10-13 23:49:58 +02:00
- fs - a remote name string e . g . "drive:"
- remote - a path within that remote e . g . "dir"
2018-10-26 15:51:28 +02:00
- opt - a dictionary of options to control the listing ( optional )
- recurse - If set recurse directories
- noModTime - If set return modification time
- showEncrypted - If set show decrypted names
- showOrigIDs - If set show the IDs for each item if known
- showHash - If set return a dictionary of hashes
2020-09-23 18:20:28 +02:00
- noMimeType - If set don ' t show mime types
- dirsOnly - If set only show directories
- filesOnly - If set only show files
2022-05-24 12:16:29 +02:00
- metadata - If set return metadata of objects also
2020-09-23 18:20:28 +02:00
- hashTypes - array of strings of hash types to show if showHash set
2018-10-26 15:51:28 +02:00
2021-08-16 11:30:01 +02:00
Returns :
2018-10-26 15:51:28 +02:00
- list
- This is an array of objects as described in the lsjson command
2022-06-19 15:51:37 +02:00
See the [ lsjson ] ( / commands / rclone_lsjson / ) command for more information on the above and examples .
2018-10-26 15:51:28 +02:00
` ,
} )
}
// List the directory
2019-06-17 10:34:30 +02:00
func rcList ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
2020-11-05 16:18:51 +01:00
f , remote , err := rc . GetFsAndRemote ( ctx , in )
2018-10-26 15:51:28 +02:00
if err != nil {
return nil , err
}
var opt ListJSONOpt
err = in . GetStruct ( "opt" , & opt )
if rc . NotErrParamNotFound ( err ) {
return nil , err
}
var list = [ ] * ListJSONItem { }
2019-06-17 10:34:30 +02:00
err = ListJSON ( ctx , f , remote , & opt , func ( item * ListJSONItem ) error {
2018-10-26 15:51:28 +02:00
list = append ( list , item )
return nil
} )
if err != nil {
return nil , err
}
out = make ( rc . Params )
out [ "list" ] = list
return out , nil
}
2020-09-23 18:20:28 +02:00
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/stat" ,
AuthRequired : true ,
Fn : rcStat ,
Title : "Give information about the supplied file or directory" ,
Help : ` This takes the following parameters
- fs - a remote name string eg "drive:"
- remote - a path within that remote eg "dir"
- opt - a dictionary of options to control the listing ( optional )
- see operations / list for the options
The result is
- item - an object as described in the lsjson command . Will be null if not found .
Note that if you are only interested in files then it is much more
efficient to set the filesOnly flag in the options .
2022-06-19 15:51:37 +02:00
See the [ lsjson ] ( / commands / rclone_lsjson / ) command for more information on the above and examples .
2020-09-23 18:20:28 +02:00
` ,
} )
}
// List the directory
func rcStat ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
f , remote , err := rc . GetFsAndRemote ( ctx , in )
if err != nil {
return nil , err
}
var opt ListJSONOpt
err = in . GetStruct ( "opt" , & opt )
if rc . NotErrParamNotFound ( err ) {
return nil , err
}
item , err := StatJSON ( ctx , f , remote , & opt )
if err != nil {
return nil , err
}
out = make ( rc . Params )
out [ "item" ] = item
return out , nil
}
2018-10-26 15:51:28 +02:00
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/about" ,
AuthRequired : true ,
Fn : rcAbout ,
Title : "Return the space used on the remote" ,
2021-08-16 11:30:01 +02:00
Help : ` This takes the following parameters :
2018-10-26 15:51:28 +02:00
2020-10-13 23:49:58 +02:00
- fs - a remote name string e . g . "drive:"
2018-10-26 15:51:28 +02:00
The result is as returned from rclone about -- json
2019-06-08 10:19:07 +02:00
2022-06-19 15:51:37 +02:00
See the [ about ] ( / commands / rclone_about / ) command for more information on the above .
2018-10-26 15:51:28 +02:00
` ,
} )
}
// About the remote
2019-06-17 10:34:30 +02:00
func rcAbout ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
2020-11-05 16:18:51 +01:00
f , err := rc . GetFs ( ctx , in )
2018-10-26 15:51:28 +02:00
if err != nil {
return nil , err
}
doAbout := f . Features ( ) . About
if doAbout == nil {
2021-11-04 11:12:57 +01:00
return nil , fmt . Errorf ( "%v doesn't support about" , f )
2018-10-26 15:51:28 +02:00
}
2019-06-17 10:34:30 +02:00
u , err := doAbout ( ctx )
2018-10-26 15:51:28 +02:00
if err != nil {
2021-11-04 11:12:57 +01:00
return nil , fmt . Errorf ( "about call failed: %w" , err )
2018-10-26 15:51:28 +02:00
}
err = rc . Reshape ( & out , u )
if err != nil {
2021-11-04 11:12:57 +01:00
return nil , fmt . Errorf ( "about Reshape failed: %w" , err )
2018-10-26 15:51:28 +02:00
}
return out , nil
}
func init ( ) {
for _ , copy := range [ ] bool { false , true } {
copy := copy
name := "Move"
if copy {
name = "Copy"
}
rc . Add ( rc . Call {
Path : "operations/" + strings . ToLower ( name ) + "file" ,
AuthRequired : true ,
2019-06-17 10:34:30 +02:00
Fn : func ( ctx context . Context , in rc . Params ) ( rc . Params , error ) {
return rcMoveOrCopyFile ( ctx , in , copy )
2018-10-26 15:51:28 +02:00
} ,
Title : name + " a file from source remote to destination remote" ,
2021-08-16 11:30:01 +02:00
Help : ` This takes the following parameters :
2018-10-26 15:51:28 +02:00
2023-03-24 12:25:39 +01:00
- srcFs - a remote name string e . g . "drive:" for the source , "/" for local filesystem
2020-10-13 23:49:58 +02:00
- srcRemote - a path within that remote e . g . "file.txt" for the source
2023-03-24 12:25:39 +01:00
- dstFs - a remote name string e . g . "drive2:" for the destination , "/" for local filesystem
2020-10-13 23:49:58 +02:00
- dstRemote - a path within that remote e . g . "file2.txt" for the destination
2018-10-26 15:51:28 +02:00
` ,
} )
}
}
// Copy a file
2019-06-17 10:34:30 +02:00
func rcMoveOrCopyFile ( ctx context . Context , in rc . Params , cp bool ) ( out rc . Params , err error ) {
2020-11-05 16:18:51 +01:00
srcFs , srcRemote , err := rc . GetFsAndRemoteNamed ( ctx , in , "srcFs" , "srcRemote" )
2018-10-26 15:51:28 +02:00
if err != nil {
return nil , err
}
2020-11-05 16:18:51 +01:00
dstFs , dstRemote , err := rc . GetFsAndRemoteNamed ( ctx , in , "dstFs" , "dstRemote" )
2018-10-26 15:51:28 +02:00
if err != nil {
return nil , err
}
2019-06-17 10:34:30 +02:00
return nil , moveOrCopyFile ( ctx , dstFs , srcFs , dstRemote , srcRemote , cp )
2018-10-26 15:51:28 +02:00
}
func init ( ) {
for _ , op := range [ ] struct {
2020-06-05 16:26:46 +02:00
name string
title string
help string
noRemote bool
needsRequest bool
2018-10-26 15:51:28 +02:00
} {
{ name : "mkdir" , title : "Make a destination directory or container" } ,
{ name : "rmdir" , title : "Remove an empty directory or container" } ,
{ name : "purge" , title : "Remove a directory or container and all of its contents" } ,
2022-06-19 15:51:37 +02:00
{ name : "rmdirs" , title : "Remove all the empty directories in the path" , help : "- leaveRoot - boolean, set to true not to delete the root\n" } ,
2018-10-26 15:51:28 +02:00
{ name : "delete" , title : "Remove files in the path" , noRemote : true } ,
{ name : "deletefile" , title : "Remove the single file pointed to" } ,
2022-06-19 15:51:37 +02:00
{ name : "copyurl" , title : "Copy the URL to the object" , help : "- url - string, URL to read from\n - autoFilename - boolean, set to true to retrieve destination file name from url\n" } ,
{ name : "uploadfile" , title : "Upload file using multiform/form-data" , help : "- each part in body represents a file to be uploaded\n" , needsRequest : true } ,
2018-10-26 15:51:28 +02:00
{ name : "cleanup" , title : "Remove trashed files in the remote or path" , noRemote : true } ,
2023-09-05 19:57:23 +02:00
{ name : "settier" , title : "Changes storage tier or class on all files in the path" , noRemote : true } ,
{ name : "settierfile" , title : "Changes storage tier or class on the single file pointed to" } ,
2018-10-26 15:51:28 +02:00
} {
op := op
2020-10-13 23:49:58 +02:00
remote := "- remote - a path within that remote e.g. \"dir\"\n"
2018-10-26 15:51:28 +02:00
if op . noRemote {
remote = ""
}
rc . Add ( rc . Call {
Path : "operations/" + op . name ,
AuthRequired : true ,
2020-06-05 16:26:46 +02:00
NeedsRequest : op . needsRequest ,
2019-06-17 10:34:30 +02:00
Fn : func ( ctx context . Context , in rc . Params ) ( rc . Params , error ) {
return rcSingleCommand ( ctx , in , op . name , op . noRemote )
2018-10-26 15:51:28 +02:00
} ,
Title : op . title ,
2021-08-16 11:30:01 +02:00
Help : ` This takes the following parameters :
2018-10-26 15:51:28 +02:00
2020-10-13 23:49:58 +02:00
- fs - a remote name string e . g . "drive:"
2018-10-26 15:51:28 +02:00
` + remote + op.help + `
2022-06-19 15:51:37 +02:00
See the [ ` + op.name + ` ] ( / commands / rclone_ ` + op.name + ` / ) command for more information on the above .
2018-10-26 15:51:28 +02:00
` ,
} )
}
}
2020-10-13 23:49:58 +02:00
// Run a single command, e.g. Mkdir
2019-06-17 10:34:30 +02:00
func rcSingleCommand ( ctx context . Context , in rc . Params , name string , noRemote bool ) ( out rc . Params , err error ) {
2018-10-26 15:51:28 +02:00
var (
f fs . Fs
remote string
)
if noRemote {
2020-11-05 16:18:51 +01:00
f , err = rc . GetFs ( ctx , in )
2018-10-26 15:51:28 +02:00
} else {
2020-11-05 16:18:51 +01:00
f , remote , err = rc . GetFsAndRemote ( ctx , in )
2018-10-26 15:51:28 +02:00
}
if err != nil {
return nil , err
}
switch name {
case "mkdir" :
2019-06-17 10:34:30 +02:00
return nil , Mkdir ( ctx , f , remote )
2018-10-26 15:51:28 +02:00
case "rmdir" :
2019-06-17 10:34:30 +02:00
return nil , Rmdir ( ctx , f , remote )
2018-10-26 15:51:28 +02:00
case "purge" :
2019-06-17 10:34:30 +02:00
return nil , Purge ( ctx , f , remote )
2018-10-26 15:51:28 +02:00
case "rmdirs" :
leaveRoot , err := in . GetBool ( "leaveRoot" )
if rc . NotErrParamNotFound ( err ) {
return nil , err
}
2019-06-17 10:34:30 +02:00
return nil , Rmdirs ( ctx , f , remote , leaveRoot )
2018-10-26 15:51:28 +02:00
case "delete" :
2019-06-17 10:34:30 +02:00
return nil , Delete ( ctx , f )
2018-10-26 15:51:28 +02:00
case "deletefile" :
2019-06-17 10:34:30 +02:00
o , err := f . NewObject ( ctx , remote )
2018-10-26 15:51:28 +02:00
if err != nil {
return nil , err
}
2019-06-17 10:34:30 +02:00
return nil , DeleteFile ( ctx , o )
2018-10-26 15:51:28 +02:00
case "copyurl" :
url , err := in . GetString ( "url" )
if err != nil {
return nil , err
}
2019-09-03 18:25:19 +02:00
autoFilename , _ := in . GetBool ( "autoFilename" )
2020-04-19 13:40:17 +02:00
noClobber , _ := in . GetBool ( "noClobber" )
2021-07-28 18:05:21 +02:00
headerFilename , _ := in . GetBool ( "headerFilename" )
2019-09-03 18:25:19 +02:00
2021-07-28 18:05:21 +02:00
_ , err = CopyURL ( ctx , f , remote , url , autoFilename , headerFilename , noClobber )
2018-10-26 15:51:28 +02:00
return nil , err
2020-06-12 08:04:30 +02:00
case "uploadfile" :
var request * http . Request
request , err := in . GetHTTPRequest ( )
if err != nil {
return nil , err
}
contentType := request . Header . Get ( "Content-Type" )
mediaType , params , err := mime . ParseMediaType ( contentType )
if err != nil {
return nil , err
}
if strings . HasPrefix ( mediaType , "multipart/" ) {
mr := multipart . NewReader ( request . Body , params [ "boundary" ] )
for {
p , err := mr . NextPart ( )
if err == io . EOF {
return nil , nil
}
if err != nil {
return nil , err
}
if p . FileName ( ) != "" {
2022-11-08 18:42:18 +01:00
obj , err := Rcat ( ctx , f , path . Join ( remote , p . FileName ( ) ) , p , time . Now ( ) , nil )
2020-06-12 08:04:30 +02:00
if err != nil {
return nil , err
}
fs . Debugf ( obj , "Upload Succeeded" )
}
}
}
return nil , nil
2018-10-26 15:51:28 +02:00
case "cleanup" :
2019-06-17 10:34:30 +02:00
return nil , CleanUp ( ctx , f )
2023-09-05 19:57:23 +02:00
case "settier" :
if ! f . Features ( ) . SetTier {
return nil , fmt . Errorf ( "remote %s does not support settier" , f . Name ( ) )
}
tier , err := in . GetString ( "tier" )
if err != nil {
return nil , err
}
return nil , SetTier ( ctx , f , tier )
case "settierfile" :
if ! f . Features ( ) . SetTier {
return nil , fmt . Errorf ( "remote %s does not support settier" , f . Name ( ) )
}
tier , err := in . GetString ( "tier" )
if err != nil {
return nil , err
}
o , err := f . NewObject ( ctx , remote )
if err != nil {
return nil , err
}
return nil , SetTierFile ( ctx , o , tier )
2018-10-26 15:51:28 +02:00
}
panic ( "unknown rcSingleCommand type" )
}
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/size" ,
AuthRequired : true ,
Fn : rcSize ,
Title : "Count the number of bytes and files in remote" ,
2021-08-16 11:30:01 +02:00
Help : ` This takes the following parameters :
2018-10-26 15:51:28 +02:00
2020-10-13 23:49:58 +02:00
- fs - a remote name string e . g . "drive:path/to/dir"
2018-10-26 15:51:28 +02:00
2021-08-16 11:30:01 +02:00
Returns :
2018-10-26 15:51:28 +02:00
- count - number of files
- bytes - number of bytes in those files
2022-06-19 15:51:37 +02:00
See the [ size ] ( / commands / rclone_size / ) command for more information on the above .
2018-10-26 15:51:28 +02:00
` ,
} )
}
2019-03-11 17:59:27 +01:00
// Size a directory
2019-06-17 10:34:30 +02:00
func rcSize ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
2020-11-05 16:18:51 +01:00
f , err := rc . GetFs ( ctx , in )
2018-10-26 15:51:28 +02:00
if err != nil {
return nil , err
}
2022-04-06 14:15:07 +02:00
count , bytes , sizeless , err := Count ( ctx , f )
2018-10-26 15:51:28 +02:00
if err != nil {
return nil , err
}
out = make ( rc . Params )
out [ "count" ] = count
out [ "bytes" ] = bytes
2022-04-06 14:15:07 +02:00
out [ "sizeless" ] = sizeless
2018-10-26 15:51:28 +02:00
return out , nil
}
2019-03-11 17:59:27 +01:00
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/publiclink" ,
AuthRequired : true ,
Fn : rcPublicLink ,
Title : "Create or retrieve a public link to the given file or folder." ,
2021-08-16 11:30:01 +02:00
Help : ` This takes the following parameters :
2019-03-11 17:59:27 +01:00
2020-10-13 23:49:58 +02:00
- fs - a remote name string e . g . "drive:"
- remote - a path within that remote e . g . "dir"
2020-05-31 23:18:01 +02:00
- unlink - boolean - if set removes the link rather than adding it ( optional )
2020-10-13 23:49:58 +02:00
- expire - string - the expiry time of the link e . g . "1d" ( optional )
2019-03-11 17:59:27 +01:00
2021-08-16 11:30:01 +02:00
Returns :
2019-03-11 17:59:27 +01:00
- url - URL of the resource
2022-06-19 15:51:37 +02:00
See the [ link ] ( / commands / rclone_link / ) command for more information on the above .
2019-03-11 17:59:27 +01:00
` ,
} )
}
// Make a public link
2019-06-17 10:34:30 +02:00
func rcPublicLink ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
2020-11-05 16:18:51 +01:00
f , remote , err := rc . GetFsAndRemote ( ctx , in )
2019-03-11 17:59:27 +01:00
if err != nil {
return nil , err
}
2020-05-31 23:18:01 +02:00
unlink , _ := in . GetBool ( "unlink" )
expire , err := in . GetDuration ( "expire" )
2021-11-09 11:18:49 +01:00
if rc . IsErrParamNotFound ( err ) {
expire = time . Duration ( fs . DurationOff )
} else if err != nil {
2020-05-31 23:18:01 +02:00
return nil , err
}
url , err := PublicLink ( ctx , f , remote , fs . Duration ( expire ) , unlink )
2019-03-11 17:59:27 +01:00
if err != nil {
return nil , err
}
out = make ( rc . Params )
out [ "url" ] = url
return out , nil
}
2019-06-08 10:19:07 +02:00
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/fsinfo" ,
Fn : rcFsInfo ,
Title : "Return information about the remote" ,
2021-08-16 11:30:01 +02:00
Help : ` This takes the following parameters :
2019-06-08 10:19:07 +02:00
2020-10-13 23:49:58 +02:00
- fs - a remote name string e . g . "drive:"
2019-06-08 10:19:07 +02:00
This returns info about the remote passed in ;
` + " ` ` ` " + `
{
2022-06-22 16:56:41 +02:00
// optional features and whether they are available or not
"Features" : {
"About" : true ,
"BucketBased" : false ,
"BucketBasedRootOK" : false ,
"CanHaveEmptyDirectories" : true ,
"CaseInsensitive" : false ,
"ChangeNotify" : false ,
"CleanUp" : false ,
"Command" : true ,
"Copy" : false ,
"DirCacheFlush" : false ,
"DirMove" : true ,
"Disconnect" : false ,
"DuplicateFiles" : false ,
"GetTier" : false ,
"IsLocal" : true ,
"ListR" : false ,
"MergeDirs" : false ,
"MetadataInfo" : true ,
"Move" : true ,
"OpenWriterAt" : true ,
"PublicLink" : false ,
"Purge" : true ,
"PutStream" : true ,
"PutUnchecked" : false ,
"ReadMetadata" : true ,
"ReadMimeType" : false ,
"ServerSideAcrossConfigs" : false ,
"SetTier" : false ,
"SetWrapper" : false ,
"Shutdown" : false ,
"SlowHash" : true ,
"SlowModTime" : false ,
"UnWrap" : false ,
"UserInfo" : false ,
"UserMetadata" : true ,
"WrapFs" : false ,
"WriteMetadata" : true ,
"WriteMimeType" : false
} ,
// Names of hashes available
"Hashes" : [
"md5" ,
"sha1" ,
"whirlpool" ,
"crc32" ,
"sha256" ,
"dropbox" ,
"mailru" ,
"quickxor"
] ,
"Name" : "local" , // Name as created
"Precision" : 1 , // Precision of timestamps in ns
"Root" : "/" , // Path as created
"String" : "Local file system at /" , // how the remote will appear in logs
// Information about the system metadata for this backend
"MetadataInfo" : {
"System" : {
"atime" : {
"Help" : "Time of last access" ,
"Type" : "RFC 3339" ,
"Example" : "2006-01-02T15:04:05.999999999Z07:00"
} ,
"btime" : {
"Help" : "Time of file birth (creation)" ,
"Type" : "RFC 3339" ,
"Example" : "2006-01-02T15:04:05.999999999Z07:00"
} ,
"gid" : {
"Help" : "Group ID of owner" ,
"Type" : "decimal number" ,
"Example" : "500"
} ,
"mode" : {
"Help" : "File type and mode" ,
"Type" : "octal, unix style" ,
"Example" : "0100664"
} ,
"mtime" : {
"Help" : "Time of last modification" ,
"Type" : "RFC 3339" ,
"Example" : "2006-01-02T15:04:05.999999999Z07:00"
} ,
"rdev" : {
"Help" : "Device ID (if special file)" ,
"Type" : "hexadecimal" ,
"Example" : "1abc"
} ,
"uid" : {
"Help" : "User ID of owner" ,
"Type" : "decimal number" ,
"Example" : "500"
}
} ,
"Help" : "Textual help string\n"
}
2019-06-08 10:19:07 +02:00
}
` + " ` ` ` " + `
This command does not have a command line equivalent so use this instead :
rclone rc -- loopback operations / fsinfo fs = remote :
` ,
} )
}
// Fsinfo the remote
2019-06-17 10:34:30 +02:00
func rcFsInfo ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
2020-11-05 16:18:51 +01:00
f , err := rc . GetFs ( ctx , in )
2019-06-08 10:19:07 +02:00
if err != nil {
return nil , err
}
info := GetFsInfo ( f )
err = rc . Reshape ( & out , info )
if err != nil {
2021-11-04 11:12:57 +01:00
return nil , fmt . Errorf ( "fsinfo Reshape failed: %w" , err )
2019-06-08 10:19:07 +02:00
}
return out , nil
}
2020-04-28 14:02:19 +02:00
func init ( ) {
rc . Add ( rc . Call {
Path : "backend/command" ,
AuthRequired : true ,
Fn : rcBackend ,
Title : "Runs a backend command." ,
2021-08-16 11:30:01 +02:00
Help : ` This takes the following parameters :
2020-04-28 14:02:19 +02:00
- command - a string with the command name
2020-10-13 23:49:58 +02:00
- fs - a remote name string e . g . "drive:"
2020-04-28 14:02:19 +02:00
- arg - a list of arguments for the backend command
- opt - a map of string to string of options
2021-08-16 11:30:01 +02:00
Returns :
2020-04-28 14:02:19 +02:00
- result - result from the backend command
2021-08-16 11:30:01 +02:00
Example :
2020-04-28 14:02:19 +02:00
rclone rc backend / command command = noop fs = . - o echo = yes - o blue - a path1 - a path2
Returns
` + " ` ` ` " + `
{
"result" : {
"arg" : [
"path1" ,
"path2"
] ,
"name" : "noop" ,
"opt" : {
"blue" : "" ,
"echo" : "yes"
}
}
}
` + " ` ` ` " + `
Note that this is the direct equivalent of using this "backend"
command :
rclone backend noop . - o echo = yes - o blue path1 path2
2020-05-20 12:39:20 +02:00
Note that arguments must be preceded by the "-a" flag
2020-04-28 14:02:19 +02:00
See the [ backend ] ( / commands / rclone_backend / ) command for more information .
` ,
} )
}
// Make a public link
func rcBackend ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
2020-11-05 16:18:51 +01:00
f , err := rc . GetFs ( ctx , in )
2020-04-28 14:02:19 +02:00
if err != nil {
return nil , err
}
doCommand := f . Features ( ) . Command
if doCommand == nil {
2021-11-04 11:12:57 +01:00
return nil , fmt . Errorf ( "%v: doesn't support backend commands" , f )
2020-04-28 14:02:19 +02:00
}
command , err := in . GetString ( "command" )
if err != nil {
return nil , err
}
var opt = map [ string ] string { }
err = in . GetStructMissingOK ( "opt" , & opt )
if err != nil {
return nil , err
}
var arg = [ ] string { }
err = in . GetStructMissingOK ( "arg" , & arg )
if err != nil {
return nil , err
}
2024-03-25 18:56:33 +01:00
result , err := doCommand ( ctx , command , arg , opt )
2020-04-28 14:02:19 +02:00
if err != nil {
2021-11-04 11:12:57 +01:00
return nil , fmt . Errorf ( "command %q failed: %w" , command , err )
2020-04-28 14:02:19 +02:00
}
out = make ( rc . Params )
out [ "result" ] = result
return out , nil
}
2023-09-05 11:54:01 +02:00
// This should really be in fs/rc/internal.go but can't go there due
// to a circular dependency on config.
func init ( ) {
rc . Add ( rc . Call {
Path : "core/du" ,
Fn : rcDu ,
Title : "Returns disk usage of a locally attached disk." ,
Help : `
This returns the disk usage for the local directory passed in as dir .
If the directory is not passed in , it defaults to the directory
pointed to by -- cache - dir .
- dir - string ( optional )
Returns :
` + " ` ` ` " + `
{
"dir" : "/" ,
"info" : {
"Available" : 361769115648 ,
"Free" : 361785892864 ,
"Total" : 982141468672
}
}
` + " ` ` ` " + `
` ,
} )
}
// Terminates app
func rcDu ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
dir , err := in . GetString ( "dir" )
if rc . IsErrParamNotFound ( err ) {
dir = config . GetCacheDir ( )
} else if err != nil {
return nil , err
}
info , err := diskusage . New ( dir )
if err != nil {
return nil , err
}
out = rc . Params {
"dir" : dir ,
"info" : info ,
}
return out , nil
}
2023-05-24 11:51:30 +02:00
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/check" ,
AuthRequired : true ,
Fn : rcCheck ,
Title : "check the source and destination are the same" ,
Help : ` Checks the files in the source and destination match . It compares
sizes and hashes and logs a report of files that don ' t
match . It doesn ' t alter the source or destination .
This takes the following parameters :
- srcFs - a remote name string e . g . "drive:" for the source , "/" for local filesystem
- dstFs - a remote name string e . g . "drive2:" for the destination , "/" for local filesystem
- download - check by downloading rather than with hash
- checkFileHash - treat checkFileFs : checkFileRemote as a SUM file with hashes of given type
- checkFileFs - treat checkFileFs : checkFileRemote as a SUM file with hashes of given type
- checkFileRemote - treat checkFileFs : checkFileRemote as a SUM file with hashes of given type
- oneWay - check one way only , source files must exist on remote
- combined - make a combined report of changes ( default false )
- missingOnSrc - report all files missing from the source ( default true )
- missingOnDst - report all files missing from the destination ( default true )
- match - report all matching files ( default false )
- differ - report all non - matching files ( default true )
- error - report all files with errors ( hashing or reading ) ( default true )
If you supply the download flag , it will download the data from
both remotes and check them against each other on the fly . This can
be useful for remotes that don ' t support hashes or if you really want
to check all the data .
If you supply the size - only global flag , it will only compare the sizes not
the hashes as well . Use this for a quick check .
If you supply the checkFileHash option with a valid hash name , the
checkFileFs : checkFileRemote must point to a text file in the SUM
format . This treats the checksum file as the source and dstFs as the
destination . Note that srcFs is not used and should not be supplied in
this case .
Returns :
- success - true if no error , false otherwise
- status - textual summary of check , OK or text string
- hashType - hash used in check , may be missing
- combined - array of strings of combined report of changes
- missingOnSrc - array of strings of all files missing from the source
- missingOnDst - array of strings of all files missing from the destination
- match - array of strings of all matching files
- differ - array of strings of all non - matching files
- error - array of strings of all files with errors ( hashing or reading )
` ,
} )
}
// Writer which writes into the slice provided
type stringWriter struct {
out * [ ] string
}
// Write writes len(p) bytes from p to the underlying data stream. It returns
// the number of bytes written from p (0 <= n <= len(p)) and any error
// encountered that caused the write to stop early. Write must return a non-nil
// error if it returns n < len(p). Write must not modify the slice data,
// even temporarily.
//
// Implementations must not retain p.
func ( s stringWriter ) Write ( p [ ] byte ) ( n int , err error ) {
result := string ( p )
result = strings . TrimSuffix ( result , "\n" )
* s . out = append ( * s . out , result )
return len ( p ) , nil
}
// Check two directories
func rcCheck ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
srcFs , err := rc . GetFsNamed ( ctx , in , "srcFs" )
if err != nil && ! rc . IsErrParamNotFound ( err ) {
return nil , err
}
dstFs , err := rc . GetFsNamed ( ctx , in , "dstFs" )
if err != nil {
return nil , err
}
checkFileFs , checkFileRemote , err := rc . GetFsAndRemoteNamed ( ctx , in , "checkFileFs" , "checkFileRemote" )
if err != nil && ! rc . IsErrParamNotFound ( err ) {
return nil , err
}
checkFileHash , err := in . GetString ( "checkFileHash" )
if err != nil && ! rc . IsErrParamNotFound ( err ) {
return nil , err
}
checkFileSet := 0
if checkFileHash != "" {
checkFileSet ++
}
if checkFileFs != nil {
checkFileSet ++
}
if checkFileRemote != "" {
checkFileSet ++
}
if checkFileSet > 0 && checkFileSet < 3 {
return nil , fmt . Errorf ( "need all of checkFileFs, checkFileRemote, checkFileHash to be set together" )
}
var checkFileHashType hash . Type
if checkFileHash != "" {
if err := checkFileHashType . Set ( checkFileHash ) ; err != nil {
return nil , err
}
if srcFs != nil {
return nil , rc . NewErrParamInvalid ( errors . New ( "only supply dstFs when using checkFileHash" ) )
}
} else {
if srcFs == nil {
return nil , rc . NewErrParamInvalid ( errors . New ( "need srcFs parameter when not using checkFileHash" ) )
}
}
oneway , _ := in . GetBool ( "oneway" )
download , _ := in . GetBool ( "download" )
opt := & CheckOpt {
Fsrc : srcFs ,
Fdst : dstFs ,
OneWay : oneway ,
}
out = rc . Params { }
getOutput := func ( name string , Default bool ) io . Writer {
active , err := in . GetBool ( name )
if err != nil {
active = Default
}
if ! active {
return nil
}
result := [ ] string { }
out [ name ] = & result
return stringWriter { & result }
}
opt . Combined = getOutput ( "combined" , false )
opt . MissingOnSrc = getOutput ( "missingOnSrc" , true )
opt . MissingOnDst = getOutput ( "missingOnDst" , true )
opt . Match = getOutput ( "match" , false )
opt . Differ = getOutput ( "differ" , true )
opt . Error = getOutput ( "error" , true )
if checkFileHash != "" {
out [ "hashType" ] = checkFileHashType . String ( )
2024-03-25 18:56:33 +01:00
err = CheckSum ( ctx , dstFs , checkFileFs , checkFileRemote , checkFileHashType , opt , download )
2023-05-24 11:51:30 +02:00
} else {
if download {
2024-03-25 18:56:33 +01:00
err = CheckDownload ( ctx , opt )
2023-05-24 11:51:30 +02:00
} else {
out [ "hashType" ] = srcFs . Hashes ( ) . Overlap ( dstFs . Hashes ( ) ) . GetOne ( ) . String ( )
2024-03-25 18:56:33 +01:00
err = Check ( ctx , opt )
2023-05-24 11:51:30 +02:00
}
}
if err != nil {
out [ "status" ] = err . Error ( )
out [ "success" ] = false
} else {
out [ "status" ] = "OK"
out [ "success" ] = true
}
return out , nil
}
2024-01-17 12:47:33 +01:00
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/hashsum" ,
AuthRequired : true ,
Fn : rcHashsum ,
Title : "Produces a hashsum file for all the objects in the path." ,
Help : ` Produces a hash file for all the objects in the path using the hash
named . The output is in the same format as the standard
md5sum / sha1sum tool .
This takes the following parameters :
- fs - a remote name string e . g . "drive:" for the source , "/" for local filesystem
- this can point to a file and just that file will be returned in the listing .
- hashType - type of hash to be used
- download - check by downloading rather than with hash ( boolean )
- base64 - output the hashes in base64 rather than hex ( boolean )
If you supply the download flag , it will download the data from the
remote and create the hash on the fly . This can be useful for remotes
that don ' t support the given hash or if you really want to check all
the data .
Note that if you wish to supply a checkfile to check hashes against
the current files then you should use operations / check instead of
operations / hashsum .
Returns :
- hashsum - array of strings of the hashes
- hashType - type of hash used
Example :
$ rclone rc -- loopback operations / hashsum fs = bin hashType = MD5 download = true base64 = true
{
"hashType" : "md5" ,
"hashsum" : [
"WTSVLpuiXyJO_kGzJerRLg== backend-versions.sh" ,
"v1b_OlWCJO9LtNq3EIKkNQ== bisect-go-rclone.sh" ,
"VHbmHzHh4taXzgag8BAIKQ== bisect-rclone.sh" ,
]
}
See the [ hashsum ] ( / commands / rclone_hashsum / ) command for more information on the above .
` ,
} )
}
// Hashsum a directory
func rcHashsum ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
ctx , f , err := rc . GetFsNamedFileOK ( ctx , in , "fs" )
if err != nil {
return nil , err
}
download , _ := in . GetBool ( "download" )
base64 , _ := in . GetBool ( "base64" )
hashType , err := in . GetString ( "hashType" )
if err != nil {
return nil , fmt . Errorf ( "%s\n%w" , hash . HelpString ( 0 ) , err )
}
var ht hash . Type
err = ht . Set ( hashType )
if err != nil {
return nil , fmt . Errorf ( "%s\n%w" , hash . HelpString ( 0 ) , err )
}
hashes := [ ] string { }
err = HashLister ( ctx , ht , base64 , download , f , stringWriter { & hashes } )
out = rc . Params {
"hashType" : ht . String ( ) ,
"hashsum" : hashes ,
}
return out , err
}