local_unix.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. //go:build linux || freebsd
  2. // Package local provides the default implementation for volumes. It
  3. // is used to mount data volume containers and directories local to
  4. // the host server.
  5. package local // import "github.com/docker/docker/volume/local"
  6. import (
  7. "fmt"
  8. "net"
  9. "os"
  10. "strings"
  11. "syscall"
  12. "time"
  13. "github.com/docker/docker/errdefs"
  14. "github.com/docker/docker/quota"
  15. units "github.com/docker/go-units"
  16. "github.com/moby/sys/mount"
  17. "github.com/moby/sys/mountinfo"
  18. "github.com/pkg/errors"
  19. )
  20. var (
  21. validOpts = map[string]struct{}{
  22. "type": {}, // specify the filesystem type for mount, e.g. nfs
  23. "o": {}, // generic mount options
  24. "device": {}, // device to mount from
  25. "size": {}, // quota size limit
  26. }
  27. mandatoryOpts = map[string][]string{
  28. "device": {"type"},
  29. "type": {"device"},
  30. "o": {"device", "type"},
  31. }
  32. )
  33. type optsConfig struct {
  34. MountType string
  35. MountOpts string
  36. MountDevice string
  37. Quota quota.Quota
  38. }
  39. func (o *optsConfig) String() string {
  40. return fmt.Sprintf("type='%s' device='%s' o='%s' size='%d'", o.MountType, o.MountDevice, o.MountOpts, o.Quota.Size)
  41. }
  42. func (r *Root) validateOpts(opts map[string]string) error {
  43. if len(opts) == 0 {
  44. return nil
  45. }
  46. for opt := range opts {
  47. if _, ok := validOpts[opt]; !ok {
  48. return errdefs.InvalidParameter(errors.Errorf("invalid option: %q", opt))
  49. }
  50. }
  51. if val, ok := opts["size"]; ok {
  52. size, err := units.RAMInBytes(val)
  53. if err != nil {
  54. return errdefs.InvalidParameter(err)
  55. }
  56. if size > 0 && r.quotaCtl == nil {
  57. return errdefs.InvalidParameter(errors.New("quota size requested but no quota support"))
  58. }
  59. }
  60. for opt, reqopts := range mandatoryOpts {
  61. if _, ok := opts[opt]; ok {
  62. for _, reqopt := range reqopts {
  63. if _, ok := opts[reqopt]; !ok {
  64. return errdefs.InvalidParameter(errors.Errorf("missing required option: %q", reqopt))
  65. }
  66. }
  67. }
  68. }
  69. return nil
  70. }
  71. func (v *localVolume) setOpts(opts map[string]string) error {
  72. if len(opts) == 0 {
  73. return nil
  74. }
  75. v.opts = &optsConfig{
  76. MountType: opts["type"],
  77. MountOpts: opts["o"],
  78. MountDevice: opts["device"],
  79. }
  80. if val, ok := opts["size"]; ok {
  81. size, err := units.RAMInBytes(val)
  82. if err != nil {
  83. return errdefs.InvalidParameter(err)
  84. }
  85. if size > 0 && v.quotaCtl == nil {
  86. return errdefs.InvalidParameter(errors.New("quota size requested but no quota support"))
  87. }
  88. v.opts.Quota.Size = uint64(size)
  89. }
  90. return v.saveOpts()
  91. }
  92. func (v *localVolume) needsMount() bool {
  93. if v.opts == nil {
  94. return false
  95. }
  96. if v.opts.MountDevice != "" || v.opts.MountType != "" {
  97. return true
  98. }
  99. return false
  100. }
  101. func (v *localVolume) mount() error {
  102. if v.opts.MountDevice == "" {
  103. return fmt.Errorf("missing device in volume options")
  104. }
  105. mountOpts := v.opts.MountOpts
  106. switch v.opts.MountType {
  107. case "nfs", "cifs":
  108. if addrValue := getAddress(v.opts.MountOpts); addrValue != "" && net.ParseIP(addrValue).To4() == nil {
  109. ipAddr, err := net.ResolveIPAddr("ip", addrValue)
  110. if err != nil {
  111. return errors.Wrapf(err, "error resolving passed in network volume address")
  112. }
  113. mountOpts = strings.Replace(mountOpts, "addr="+addrValue, "addr="+ipAddr.String(), 1)
  114. }
  115. }
  116. if err := mount.Mount(v.opts.MountDevice, v.path, v.opts.MountType, mountOpts); err != nil {
  117. if password := getPassword(v.opts.MountOpts); password != "" {
  118. err = errors.New(strings.Replace(err.Error(), "password="+password, "password=********", 1))
  119. }
  120. return errors.Wrap(err, "failed to mount local volume")
  121. }
  122. return nil
  123. }
  124. func (v *localVolume) postMount() error {
  125. if v.opts == nil {
  126. return nil
  127. }
  128. if v.opts.Quota.Size > 0 {
  129. if v.quotaCtl != nil {
  130. return v.quotaCtl.SetQuota(v.path, v.opts.Quota)
  131. } else {
  132. return errors.New("size quota requested for volume but no quota support")
  133. }
  134. }
  135. return nil
  136. }
  137. func (v *localVolume) unmount() error {
  138. if v.needsMount() {
  139. if err := mount.Unmount(v.path); err != nil {
  140. if mounted, mErr := mountinfo.Mounted(v.path); mounted || mErr != nil {
  141. return errdefs.System(err)
  142. }
  143. }
  144. v.active.mounted = false
  145. }
  146. return nil
  147. }
  148. // restoreIfMounted restores the mounted status if the _data directory is already mounted.
  149. func (v *localVolume) restoreIfMounted() error {
  150. if v.needsMount() {
  151. // Check if the _data directory is already mounted.
  152. mounted, err := mountinfo.Mounted(v.path)
  153. if err != nil {
  154. return fmt.Errorf("failed to determine if volume _data path is already mounted: %w", err)
  155. }
  156. if mounted {
  157. // Mark volume as mounted, but don't increment active count. If
  158. // any container needs this, the refcount will be incremented
  159. // by the live-restore (if enabled).
  160. // In other case, refcount will be zero but the volume will
  161. // already be considered as mounted when Mount is called, and
  162. // only the refcount will be incremented.
  163. v.active.mounted = true
  164. }
  165. }
  166. return nil
  167. }
  168. func (v *localVolume) CreatedAt() (time.Time, error) {
  169. fileInfo, err := os.Stat(v.rootPath)
  170. if err != nil {
  171. return time.Time{}, err
  172. }
  173. sec, nsec := fileInfo.Sys().(*syscall.Stat_t).Ctim.Unix()
  174. return time.Unix(sec, nsec), nil
  175. }