github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/stat-main.go (about)

     1  // Copyright (c) 2015-2022 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"context"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/fatih/color"
    26  	"github.com/minio/cli"
    27  	"github.com/minio/pkg/v2/console"
    28  )
    29  
    30  // stat specific flags.
    31  var (
    32  	statFlags = []cli.Flag{
    33  		cli.StringFlag{
    34  			Name:  "rewind",
    35  			Usage: "stat on older version(s)",
    36  		},
    37  		cli.BoolFlag{
    38  			Name:  "versions",
    39  			Usage: "stat all versions",
    40  		},
    41  		cli.StringFlag{
    42  			Name:  "version-id, vid",
    43  			Usage: "stat a specific object version",
    44  		},
    45  		cli.BoolFlag{
    46  			Name:  "recursive, r",
    47  			Usage: "stat all objects recursively",
    48  		},
    49  	}
    50  )
    51  
    52  // show object metadata
    53  var statCmd = cli.Command{
    54  	Name:         "stat",
    55  	Usage:        "show object metadata",
    56  	Action:       mainStat,
    57  	OnUsageError: onUsageError,
    58  	Before:       setGlobalsFromContext,
    59  	Flags:        append(append(statFlags, encCFlag), globalFlags...),
    60  	CustomHelpTemplate: `NAME:
    61    {{.HelpName}} - {{.Usage}}
    62  
    63  USAGE:
    64    {{.HelpName}} [FLAGS] TARGET [TARGET ...]
    65  
    66  FLAGS:
    67    {{range .VisibleFlags}}{{.}}
    68    {{end}}
    69  
    70  EXAMPLES:
    71    1. Stat all contents of mybucket on Amazon S3 cloud storage.
    72       {{.Prompt}} {{.HelpName}} s3/mybucket/
    73  
    74    2. Stat all contents of mybucket on Amazon S3 cloud storage on Microsoft Windows.
    75       {{.Prompt}} {{.HelpName}} s3\mybucket\
    76  
    77    3. Stat files recursively on a local filesystem on Microsoft Windows.
    78       {{.Prompt}} {{.HelpName}} --recursive C:\Users\mydocuments\
    79  
    80    4. Stat encrypted files on Amazon S3 cloud storage. In case the encryption key contains non-printable character like tab, pass the
    81       base64 encoded string as key.
    82       {{.Prompt}} {{.HelpName}} --enc-c "s3/personal-document/=MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDA" s3/personal-document/2019-account_report.docx
    83  
    84    5. Stat a specific object version.
    85       {{.Prompt}} {{.HelpName}} --version-id "CL3sWgdSN2pNntSf6UnZAuh2kcu8E8si" s3/personal-docs/2018-account_report.docx
    86  
    87    6. Stat all objects versions recursively created before 1st January 2020.
    88       {{.Prompt}} {{.HelpName}} --versions --rewind 2020.01.01T00:00 s3/personal-docs/
    89  `,
    90  }
    91  
    92  // parseAndCheckStatSyntax - parse and validate all the passed arguments
    93  func parseAndCheckStatSyntax(ctx context.Context, cliCtx *cli.Context, encKeyDB map[string][]prefixSSEPair) ([]string, bool, string, time.Time, bool) {
    94  	if !cliCtx.Args().Present() {
    95  		showCommandHelpAndExit(cliCtx, 1) // last argument is exit code
    96  	}
    97  
    98  	args := cliCtx.Args()
    99  	for _, arg := range args {
   100  		if strings.TrimSpace(arg) == "" {
   101  			fatalIf(errInvalidArgument().Trace(args...), "Unable to validate empty argument.")
   102  		}
   103  	}
   104  
   105  	recursive := cliCtx.Bool("recursive")
   106  	versionID := cliCtx.String("version-id")
   107  	withVersions := cliCtx.Bool("versions")
   108  	rewind := parseRewindFlag(cliCtx.String("rewind"))
   109  
   110  	// extract URLs.
   111  	URLs := cliCtx.Args()
   112  
   113  	if versionID != "" && len(args) > 1 {
   114  		fatalIf(errInvalidArgument().Trace(args...), "You cannot specify --version-id with multiple arguments.")
   115  	}
   116  
   117  	if versionID != "" && (recursive || withVersions || !rewind.IsZero()) {
   118  		fatalIf(errInvalidArgument().Trace(args...), "You cannot specify --version-id with either --rewind, --versions or --recursive.")
   119  	}
   120  
   121  	for _, url := range URLs {
   122  		_, _, err := url2Stat(ctx, url2StatOptions{urlStr: url, versionID: versionID, fileAttr: false, encKeyDB: encKeyDB, timeRef: rewind, isZip: false, ignoreBucketExistsCheck: false})
   123  		if err != nil {
   124  			fatalIf(err.Trace(url), "Unable to stat `"+url+"`.")
   125  		}
   126  	}
   127  
   128  	return URLs, recursive, versionID, rewind, withVersions
   129  }
   130  
   131  // mainStat - is a handler for mc stat command
   132  func mainStat(cliCtx *cli.Context) error {
   133  	ctx, cancelStat := context.WithCancel(globalContext)
   134  	defer cancelStat()
   135  
   136  	// Additional command specific theme customization.
   137  	console.SetColor("Name", color.New(color.Bold, color.FgCyan))
   138  	console.SetColor("Date", color.New(color.FgWhite))
   139  	console.SetColor("Size", color.New(color.FgWhite))
   140  	console.SetColor("ETag", color.New(color.FgWhite))
   141  	console.SetColor("Metadata", color.New(color.FgWhite))
   142  	// theme specific to stat bucket
   143  	console.SetColor("Key", color.New(color.FgCyan))
   144  	console.SetColor("Value", color.New(color.FgYellow))
   145  	console.SetColor("Unset", color.New(color.FgRed))
   146  	console.SetColor("Set", color.New(color.FgGreen))
   147  
   148  	console.SetColor("Title", color.New(color.Bold, color.FgBlue))
   149  	console.SetColor("Count", color.New(color.FgGreen))
   150  
   151  	// Parse encryption keys per command.
   152  	encKeyDB, err := validateAndCreateEncryptionKeys(cliCtx)
   153  	fatalIf(err, "Unable to parse encryption keys.")
   154  
   155  	// check 'stat' cli arguments.
   156  	args, isRecursive, versionID, rewind, withVersions := parseAndCheckStatSyntax(ctx, cliCtx, encKeyDB)
   157  	// mimic operating system tool behavior.
   158  	if len(args) == 0 {
   159  		args = []string{"."}
   160  	}
   161  
   162  	for _, targetURL := range args {
   163  		fatalIf(statURL(ctx, targetURL, versionID, rewind, withVersions, false, isRecursive, encKeyDB), "Unable to stat `"+targetURL+"`.")
   164  	}
   165  
   166  	return nil
   167  }