github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/osutil/mkdirallchown.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 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 "os" 24 "path/filepath" 25 "sync" 26 "syscall" 27 28 "github.com/snapcore/snapd/osutil/sys" 29 ) 30 31 // XXX: we need to come back and fix this; this is a hack to unblock us. 32 // As part of the fixing we should unify with the similar code in 33 // cmd/snap-update-ns/utils.(*Secure).MkdirAll 34 var mu sync.Mutex 35 36 // MkdirAllChown is like os.MkdirAll but it calls os.Chown on any 37 // directories it creates. 38 func MkdirAllChown(path string, perm os.FileMode, uid sys.UserID, gid sys.GroupID) error { 39 mu.Lock() 40 defer mu.Unlock() 41 return mkdirAllChown(filepath.Clean(path), perm, uid, gid) 42 } 43 44 func mkdirAllChown(path string, perm os.FileMode, uid sys.UserID, gid sys.GroupID) error { 45 // split out so filepath.Clean isn't called twice for each inner path 46 if s, err := os.Stat(path); err == nil { 47 if s.IsDir() { 48 return nil 49 } 50 51 // emulate os.MkdirAll 52 return &os.PathError{ 53 Op: "mkdir", 54 Path: path, 55 Err: syscall.ENOTDIR, 56 } 57 } 58 59 dir := filepath.Dir(path) 60 if dir != "/" { 61 if err := mkdirAllChown(dir, perm, uid, gid); err != nil { 62 return err 63 } 64 } 65 66 cand := path + ".mkdir-new" 67 68 if err := os.Mkdir(cand, perm); err != nil && !os.IsExist(err) { 69 return err 70 } 71 72 if err := sys.ChownPath(cand, uid, gid); err != nil { 73 return err 74 } 75 76 if err := os.Rename(cand, path); err != nil { 77 return err 78 } 79 80 fd, err := os.Open(dir) 81 if err != nil { 82 return err 83 } 84 defer fd.Close() 85 86 return fd.Sync() 87 }