1package mount // import "github.com/docker/docker/pkg/mount"
2
3import (
4	"fmt"
5	"strings"
6)
7
8var flags = map[string]struct {
9	clear bool
10	flag  int
11}{
12	"defaults":      {false, 0},
13	"ro":            {false, RDONLY},
14	"rw":            {true, RDONLY},
15	"suid":          {true, NOSUID},
16	"nosuid":        {false, NOSUID},
17	"dev":           {true, NODEV},
18	"nodev":         {false, NODEV},
19	"exec":          {true, NOEXEC},
20	"noexec":        {false, NOEXEC},
21	"sync":          {false, SYNCHRONOUS},
22	"async":         {true, SYNCHRONOUS},
23	"dirsync":       {false, DIRSYNC},
24	"remount":       {false, REMOUNT},
25	"mand":          {false, MANDLOCK},
26	"nomand":        {true, MANDLOCK},
27	"atime":         {true, NOATIME},
28	"noatime":       {false, NOATIME},
29	"diratime":      {true, NODIRATIME},
30	"nodiratime":    {false, NODIRATIME},
31	"bind":          {false, BIND},
32	"rbind":         {false, RBIND},
33	"unbindable":    {false, UNBINDABLE},
34	"runbindable":   {false, RUNBINDABLE},
35	"private":       {false, PRIVATE},
36	"rprivate":      {false, RPRIVATE},
37	"shared":        {false, SHARED},
38	"rshared":       {false, RSHARED},
39	"slave":         {false, SLAVE},
40	"rslave":        {false, RSLAVE},
41	"relatime":      {false, RELATIME},
42	"norelatime":    {true, RELATIME},
43	"strictatime":   {false, STRICTATIME},
44	"nostrictatime": {true, STRICTATIME},
45}
46
47var validFlags = map[string]bool{
48	"":          true,
49	"size":      true,
50	"mode":      true,
51	"uid":       true,
52	"gid":       true,
53	"nr_inodes": true,
54	"nr_blocks": true,
55	"mpol":      true,
56}
57
58var propagationFlags = map[string]bool{
59	"bind":        true,
60	"rbind":       true,
61	"unbindable":  true,
62	"runbindable": true,
63	"private":     true,
64	"rprivate":    true,
65	"shared":      true,
66	"rshared":     true,
67	"slave":       true,
68	"rslave":      true,
69}
70
71// MergeTmpfsOptions merge mount options to make sure there is no duplicate.
72func MergeTmpfsOptions(options []string) ([]string, error) {
73	// We use collisions maps to remove duplicates.
74	// For flag, the key is the flag value (the key for propagation flag is -1)
75	// For data=value, the key is the data
76	flagCollisions := map[int]bool{}
77	dataCollisions := map[string]bool{}
78
79	var newOptions []string
80	// We process in reverse order
81	for i := len(options) - 1; i >= 0; i-- {
82		option := options[i]
83		if option == "defaults" {
84			continue
85		}
86		if f, ok := flags[option]; ok && f.flag != 0 {
87			// There is only one propagation mode
88			key := f.flag
89			if propagationFlags[option] {
90				key = -1
91			}
92			// Check to see if there is collision for flag
93			if !flagCollisions[key] {
94				// We prepend the option and add to collision map
95				newOptions = append([]string{option}, newOptions...)
96				flagCollisions[key] = true
97			}
98			continue
99		}
100		opt := strings.SplitN(option, "=", 2)
101		if len(opt) != 2 || !validFlags[opt[0]] {
102			return nil, fmt.Errorf("Invalid tmpfs option %q", opt)
103		}
104		if !dataCollisions[opt[0]] {
105			// We prepend the option and add to collision map
106			newOptions = append([]string{option}, newOptions...)
107			dataCollisions[opt[0]] = true
108		}
109	}
110
111	return newOptions, nil
112}
113
114// Parse fstab type mount options into mount() flags
115// and device specific data
116func parseOptions(options string) (int, string) {
117	var (
118		flag int
119		data []string
120	)
121
122	for _, o := range strings.Split(options, ",") {
123		// If the option does not exist in the flags table or the flag
124		// is not supported on the platform,
125		// then it is a data value for a specific fs type
126		if f, exists := flags[o]; exists && f.flag != 0 {
127			if f.clear {
128				flag &= ^f.flag
129			} else {
130				flag |= f.flag
131			}
132		} else {
133			data = append(data, o)
134		}
135	}
136	return flag, strings.Join(data, ",")
137}
138