github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/osutil/mountentry_linux.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     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 General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package osutil
    21  
    22  import (
    23  	"fmt"
    24  	"strconv"
    25  	"strings"
    26  	"syscall"
    27  )
    28  
    29  // ParseMountEntry parses a fstab-like entry.
    30  func ParseMountEntry(s string) (MountEntry, error) {
    31  	var e MountEntry
    32  	var err error
    33  	var df, cpn int
    34  	fields := strings.FieldsFunc(s, func(r rune) bool { return r == ' ' || r == '\t' })
    35  	// Look for any inline comments. The first field that starts with '#' is a comment.
    36  	for i, field := range fields {
    37  		if strings.HasPrefix(field, "#") {
    38  			fields = fields[:i]
    39  			break
    40  		}
    41  	}
    42  	// Do all error checks before any assignments to `e'
    43  	if len(fields) < 3 || len(fields) > 6 {
    44  		return e, fmt.Errorf("expected between 3 and 6 fields, found %d", len(fields))
    45  	}
    46  	e.Name = unescape(fields[0])
    47  	e.Dir = unescape(fields[1])
    48  	e.Type = unescape(fields[2])
    49  	// Parse Options if we have at least 4 fields
    50  	if len(fields) > 3 {
    51  		e.Options = strings.Split(unescape(fields[3]), ",")
    52  	}
    53  	// Parse DumpFrequency if we have at least 5 fields
    54  	if len(fields) > 4 {
    55  		df, err = strconv.Atoi(fields[4])
    56  		if err != nil {
    57  			return e, fmt.Errorf("cannot parse dump frequency: %q", fields[4])
    58  		}
    59  	}
    60  	e.DumpFrequency = df
    61  	// Parse CheckPassNumber if we have at least 6 fields
    62  	if len(fields) > 5 {
    63  		cpn, err = strconv.Atoi(fields[5])
    64  		if err != nil {
    65  			return e, fmt.Errorf("cannot parse check pass number: %q", fields[5])
    66  		}
    67  	}
    68  	e.CheckPassNumber = cpn
    69  	return e, nil
    70  }
    71  
    72  // MountOptsToCommonFlags converts mount options strings to a mount flag,
    73  // returning unparsed flags. The unparsed flags will not contain any snapd-
    74  // specific mount option, those starting with the string "x-snapd."
    75  func MountOptsToCommonFlags(opts []string) (flags int, unparsed []string) {
    76  	for _, opt := range opts {
    77  		switch opt {
    78  		case "rw":
    79  			// There's no flag for rw
    80  		case "ro":
    81  			flags |= syscall.MS_RDONLY
    82  		case "nosuid":
    83  			flags |= syscall.MS_NOSUID
    84  		case "nodev":
    85  			flags |= syscall.MS_NODEV
    86  		case "noexec":
    87  			flags |= syscall.MS_NOEXEC
    88  		case "sync":
    89  			flags |= syscall.MS_SYNCHRONOUS
    90  		case "remount":
    91  			flags |= syscall.MS_REMOUNT
    92  		case "mand":
    93  			flags |= syscall.MS_MANDLOCK
    94  		case "dirsync":
    95  			flags |= syscall.MS_DIRSYNC
    96  		case "noatime":
    97  			flags |= syscall.MS_NOATIME
    98  		case "nodiratime":
    99  			flags |= syscall.MS_NODIRATIME
   100  		case "bind":
   101  			flags |= syscall.MS_BIND
   102  		case "rbind":
   103  			flags |= syscall.MS_BIND | syscall.MS_REC
   104  		case "move":
   105  			flags |= syscall.MS_MOVE
   106  		case "silent":
   107  			flags |= syscall.MS_SILENT
   108  		case "acl":
   109  			flags |= syscall.MS_POSIXACL
   110  		case "private":
   111  			flags |= syscall.MS_PRIVATE
   112  		case "rprivate":
   113  			flags |= syscall.MS_PRIVATE | syscall.MS_REC
   114  		case "slave":
   115  			flags |= syscall.MS_SLAVE
   116  		case "rslave":
   117  			flags |= syscall.MS_SLAVE | syscall.MS_REC
   118  		case "shared":
   119  			flags |= syscall.MS_SHARED
   120  		case "rshared":
   121  			flags |= syscall.MS_SHARED | syscall.MS_REC
   122  		case "relatime":
   123  			flags |= syscall.MS_RELATIME
   124  		case "strictatime":
   125  			flags |= syscall.MS_STRICTATIME
   126  		default:
   127  			if !strings.HasPrefix(opt, "x-snapd.") {
   128  				unparsed = append(unparsed, opt)
   129  			}
   130  		}
   131  	}
   132  	return flags, unparsed
   133  }
   134  
   135  // MountOptsToFlags converts mount options strings to a mount flag.
   136  func MountOptsToFlags(opts []string) (flags int, err error) {
   137  	flags, unparsed := MountOptsToCommonFlags(opts)
   138  	for _, opt := range unparsed {
   139  		if !strings.HasPrefix(opt, "x-snapd.") {
   140  			return 0, fmt.Errorf("unsupported mount option: %q", opt)
   141  		}
   142  	}
   143  	return flags, nil
   144  }