1/*
2Copyright (c) 2017 VMware, Inc. All Rights Reserved.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package simulator
18
19import (
20	"io"
21	"os"
22	"path"
23
24	"github.com/vmware/govmomi/object"
25	"github.com/vmware/govmomi/simulator/esx"
26	"github.com/vmware/govmomi/vim25/methods"
27	"github.com/vmware/govmomi/vim25/mo"
28	"github.com/vmware/govmomi/vim25/soap"
29	"github.com/vmware/govmomi/vim25/types"
30)
31
32type FileManager struct {
33	mo.FileManager
34}
35
36func NewFileManager(ref types.ManagedObjectReference) object.Reference {
37	m := &FileManager{}
38	m.Self = ref
39	return m
40}
41
42func (f *FileManager) findDatastore(ref mo.Reference, name string) (*Datastore, types.BaseMethodFault) {
43	var refs []types.ManagedObjectReference
44
45	switch obj := ref.(type) {
46	case *Folder:
47		refs = obj.ChildEntity
48	case *StoragePod:
49		refs = obj.ChildEntity
50	}
51
52	for _, ref := range refs {
53		switch obj := Map.Get(ref).(type) {
54		case *Datastore:
55			if obj.Name == name {
56				return obj, nil
57			}
58		case *Folder, *StoragePod:
59			ds, _ := f.findDatastore(obj, name)
60			if ds != nil {
61				return ds, nil
62			}
63		}
64	}
65
66	return nil, &types.InvalidDatastore{Name: name}
67}
68
69func (f *FileManager) resolve(dc *types.ManagedObjectReference, name string) (string, types.BaseMethodFault) {
70	p, fault := parseDatastorePath(name)
71	if fault != nil {
72		return "", fault
73	}
74
75	if dc == nil {
76		if Map.IsESX() {
77			dc = &esx.Datacenter.Self
78		} else {
79			return "", &types.InvalidArgument{InvalidProperty: "dc"}
80		}
81	}
82
83	folder := Map.Get(*dc).(*Datacenter).DatastoreFolder
84
85	ds, fault := f.findDatastore(Map.Get(folder), p.Datastore)
86	if fault != nil {
87		return "", fault
88	}
89
90	dir := ds.Info.GetDatastoreInfo().Url
91
92	return path.Join(dir, p.Path), nil
93}
94
95func (f *FileManager) fault(name string, err error, fault types.BaseFileFault) types.BaseMethodFault {
96	switch {
97	case os.IsNotExist(err):
98		fault = new(types.FileNotFound)
99	case os.IsExist(err):
100		fault = new(types.FileAlreadyExists)
101	}
102
103	fault.GetFileFault().File = name
104
105	return fault.(types.BaseMethodFault)
106}
107
108func (f *FileManager) deleteDatastoreFile(req *types.DeleteDatastoreFile_Task) types.BaseMethodFault {
109	file, fault := f.resolve(req.Datacenter, req.Name)
110	if fault != nil {
111		return fault
112	}
113
114	_, err := os.Stat(file)
115	if err != nil {
116		if os.IsNotExist(err) {
117			return f.fault(file, err, new(types.CannotDeleteFile))
118		}
119	}
120
121	err = os.RemoveAll(file)
122	if err != nil {
123		return f.fault(file, err, new(types.CannotDeleteFile))
124	}
125
126	return nil
127}
128
129func (f *FileManager) DeleteDatastoreFileTask(req *types.DeleteDatastoreFile_Task) soap.HasFault {
130	task := CreateTask(f, "deleteDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) {
131		return nil, f.deleteDatastoreFile(req)
132	})
133
134	return &methods.DeleteDatastoreFile_TaskBody{
135		Res: &types.DeleteDatastoreFile_TaskResponse{
136			Returnval: task.Run(),
137		},
138	}
139}
140
141func (f *FileManager) MakeDirectory(req *types.MakeDirectory) soap.HasFault {
142	body := &methods.MakeDirectoryBody{}
143
144	name, fault := f.resolve(req.Datacenter, req.Name)
145	if fault != nil {
146		body.Fault_ = Fault("", fault)
147		return body
148	}
149
150	mkdir := os.Mkdir
151
152	if isTrue(req.CreateParentDirectories) {
153		mkdir = os.MkdirAll
154	}
155
156	err := mkdir(name, 0700)
157	if err != nil {
158		fault = f.fault(req.Name, err, new(types.CannotCreateFile))
159		body.Fault_ = Fault(err.Error(), fault)
160		return body
161	}
162
163	body.Res = new(types.MakeDirectoryResponse)
164	return body
165}
166
167func (f *FileManager) moveDatastoreFile(req *types.MoveDatastoreFile_Task) types.BaseMethodFault {
168	src, fault := f.resolve(req.SourceDatacenter, req.SourceName)
169	if fault != nil {
170		return fault
171	}
172
173	dst, fault := f.resolve(req.DestinationDatacenter, req.DestinationName)
174	if fault != nil {
175		return fault
176	}
177
178	if !isTrue(req.Force) {
179		_, err := os.Stat(dst)
180		if err == nil {
181			return f.fault(dst, nil, new(types.FileAlreadyExistsFault))
182		}
183	}
184
185	err := os.Rename(src, dst)
186	if err != nil {
187		return f.fault(src, err, new(types.CannotAccessFile))
188	}
189
190	return nil
191}
192
193func (f *FileManager) MoveDatastoreFileTask(req *types.MoveDatastoreFile_Task) soap.HasFault {
194	task := CreateTask(f, "moveDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) {
195		return nil, f.moveDatastoreFile(req)
196	})
197
198	return &methods.MoveDatastoreFile_TaskBody{
199		Res: &types.MoveDatastoreFile_TaskResponse{
200			Returnval: task.Run(),
201		},
202	}
203}
204
205func (f *FileManager) copyDatastoreFile(req *types.CopyDatastoreFile_Task) types.BaseMethodFault {
206	src, fault := f.resolve(req.SourceDatacenter, req.SourceName)
207	if fault != nil {
208		return fault
209	}
210
211	dst, fault := f.resolve(req.DestinationDatacenter, req.DestinationName)
212	if fault != nil {
213		return fault
214	}
215
216	if !isTrue(req.Force) {
217		_, err := os.Stat(dst)
218		if err == nil {
219			return f.fault(dst, nil, new(types.FileAlreadyExistsFault))
220		}
221	}
222
223	r, err := os.Open(src)
224	if err != nil {
225		return f.fault(dst, err, new(types.CannotAccessFile))
226	}
227	defer r.Close()
228
229	w, err := os.Create(dst)
230	if err != nil {
231		return f.fault(dst, err, new(types.CannotCreateFile))
232	}
233	defer w.Close()
234
235	if _, err = io.Copy(w, r); err != nil {
236		return f.fault(dst, err, new(types.CannotCreateFile))
237	}
238
239	return nil
240}
241
242func (f *FileManager) CopyDatastoreFileTask(req *types.CopyDatastoreFile_Task) soap.HasFault {
243	task := CreateTask(f, "copyDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) {
244		return nil, f.copyDatastoreFile(req)
245	})
246
247	return &methods.CopyDatastoreFile_TaskBody{
248		Res: &types.CopyDatastoreFile_TaskResponse{
249			Returnval: task.Run(),
250		},
251	}
252}
253