50
50
// Get implements storage.StorageReader.Get.
51
51
func (f *fileStorageReader) Get(name string) (io.ReadCloser, error) {
52
if isInternalPath(name) {
53
return nil, &os.PathError{
52
59
filename := f.fullPath(name)
53
60
fi, err := os.Stat(filename)
76
// isInternalPath returns true if a path should be hidden from user visibility
77
// filestorage uses ".tmp/" as a staging directory for uploads, so we don't
78
// want it to be visible
79
func isInternalPath(path string) bool {
80
// This blocks both ".tmp", ".tmp/foo" but also ".tmpdir", better to be
81
// overly restrictive to start with
82
return strings.HasPrefix(path, ".tmp")
69
85
// List implements storage.StorageReader.List.
70
86
func (f *fileStorageReader) List(prefix string) ([]string, error) {
88
if isInternalPath(prefix) {
71
91
prefix = filepath.Join(f.path, prefix)
72
92
dir := filepath.Dir(prefix)
74
93
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
105
124
type fileStorageWriter struct {
106
125
fileStorageReader
110
// UseDefaultTmpDir may be passed into NewFileStorageWriter
111
// for the tmpdir argument, to signify that the default
112
// value should be used. See NewFileStorageWriter for more.
113
const UseDefaultTmpDir = ""
115
128
// NewFileStorageWriter returns a new read/write storag for
116
129
// a directory inside the local file system.
118
// A temporary directory may be specified, in which files will be written
119
// to before moving to the final destination. If specified, the temporary
120
// directory should be on the same filesystem as the storage directory
121
// to ensure atomicity. If tmpdir == UseDefaultTmpDir (""), then path+".tmp"
124
// If tmpdir == UseDefaultTmpDir, it will be created when Put is invoked,
125
// and will be removed afterwards. If tmpdir != UseDefaultTmpDir, it must
126
// already exist, and will never be removed.
127
func NewFileStorageWriter(path, tmpdir string) (storage.Storage, error) {
130
func NewFileStorageWriter(path string) (storage.Storage, error) {
128
131
reader, err := NewFileStorageReader(path)
132
return &fileStorageWriter{*reader.(*fileStorageReader), tmpdir}, nil
135
return &fileStorageWriter{*reader.(*fileStorageReader)}, nil
135
138
func (f *fileStorageWriter) Put(name string, r io.Reader, length int64) error {
139
if isInternalPath(name) {
140
return &os.PathError{
143
Err: os.ErrPermission,
136
146
fullpath := f.fullPath(name)
137
147
dir := filepath.Dir(fullpath)
139
if tmpdir == UseDefaultTmpDir {
140
tmpdir = f.path + ".tmp"
141
if err := os.MkdirAll(tmpdir, 0755); err != nil && !os.IsExist(err) {
144
defer os.Remove(tmpdir)
146
if err := os.MkdirAll(dir, 0755); err != nil && !os.IsExist(err) {
148
if err := os.MkdirAll(dir, 0755); err != nil {
151
tmpdir := filepath.Join(f.path, ".tmp")
152
if err := os.MkdirAll(tmpdir, 0755); err != nil {
155
defer os.Remove(tmpdir)
149
156
// Write to a temporary file first, and then move (atomically).
150
157
file, err := ioutil.TempFile(tmpdir, "juju-filestorage-")