Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


92ed9ff3e170347d65c68fa7afa9450198d7a0d6
[palacios.git] / linux_module / iface-file.c
1 /* Palacios file interface 
2  * (c) Jack Lange, 2010
3  */
4
5
6 #include <linux/fs.h>
7 #include <linux/namei.h>
8 #include <linux/version.h>
9 #include <linux/file.h>
10 #include <linux/spinlock.h>
11 #include <linux/uaccess.h>
12 #include <linux/module.h>
13
14 #include "palacios.h"
15 #include "linux-exts.h"
16
17 #include <interfaces/vmm_file.h>
18
19 static struct list_head global_files;
20
21 #define isprint(a) ((a >= ' ') && (a <= '~'))
22
23 struct palacios_file {
24     struct file * filp;
25
26     char * path;
27     int mode;
28     
29     spinlock_t lock;
30
31     struct v3_guest * guest;
32     
33
34     struct list_head file_node;
35 };
36
37
38 // Currently this just holds the list of open files
39 struct vm_file_state {
40     struct list_head open_files;
41 };
42
43
44
45 static int palacios_file_mkdir(const char * pathname, unsigned short perms, int recurse);
46
47 static int mkdir_recursive(const char * path, unsigned short perms) {
48     char * tmp_str = NULL;
49     char * dirname_ptr;
50     char * tmp_iter;
51
52     tmp_str = palacios_alloc(strlen(path) + 1);
53     if (!tmp_str) { 
54         ERROR("Cannot allocate in mkdir recursive\n");
55         return -1;
56     }
57
58     memset(tmp_str, 0, strlen(path) + 1);
59     // will terminate tmp_str
60     strncpy(tmp_str, path, strlen(path));
61
62     dirname_ptr = tmp_str;
63     tmp_iter = tmp_str;
64
65     // parse path string, call palacios_file_mkdir recursively.
66
67
68     while (dirname_ptr != NULL) {
69         int done = 0;
70
71         while ((*tmp_iter != '/') && 
72                (*tmp_iter != '\0')) {
73
74             if ( (!isprint(*tmp_iter))) {
75                 ERROR("Invalid character in path name (%d)\n", *tmp_iter);
76                 palacios_free(tmp_str);
77                 return -1;
78             } else {
79                 tmp_iter++;
80             }
81         }
82
83         if (*tmp_iter == '/') {
84             *tmp_iter = '\0';
85         } else {
86             done = 1;
87         }
88
89         // Ignore empty directories
90         if ((tmp_iter - dirname_ptr) > 1) {
91             if (palacios_file_mkdir(tmp_str, perms, 0) != 0) {
92                 ERROR("Could not create directory (%s)\n", tmp_str);
93                 palacios_free(tmp_str);
94                 return -1;
95             }
96         }
97
98         if (done) {
99             break;
100         } else {
101             *tmp_iter = '/';
102         }
103         
104         tmp_iter++;
105
106         dirname_ptr = tmp_iter;
107     }
108     
109     palacios_free(tmp_str);
110
111     return 0;
112 }
113
114 static int palacios_file_mkdir(const char * pathname, unsigned short perms, int recurse) {
115     /* Welcome to the jungle... */
116
117 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,41)
118     /* DO NOT REFERENCE THIS VARIABLE */
119     /* It only exists to provide version compatibility */
120     struct path tmp_path; 
121 #endif
122
123     struct path * path_ptr = NULL;
124     struct dentry * dentry;
125     int ret = 0;
126
127
128
129     if (recurse != 0) {
130         return mkdir_recursive(pathname, perms);
131     } 
132
133     /* Before Linux 3.1 this was somewhat more difficult */
134 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,41)
135     {
136         struct nameidata nd;
137
138         // I'm not 100% sure about the version here, but it was around this time that the API changed
139 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38) 
140         ret = kern_path_parent(pathname, &nd);
141 #else 
142
143         if (path_lookup(pathname, LOOKUP_DIRECTORY | LOOKUP_FOLLOW, &nd) == 0) {
144             return 0;
145         }
146
147         if (path_lookup(pathname, LOOKUP_PARENT | LOOKUP_FOLLOW, &nd) != 0) {
148             return -1;
149         }
150 #endif
151
152         if (ret != 0) {
153             ERROR("%s:%d - Error: kern_path_parent() returned error for (%s)\n", __FILE__, __LINE__, 
154                    pathname);
155             return -1;
156         }
157         
158         dentry = lookup_create(&nd, 1);
159         path_ptr = &(nd.path);
160     }
161 #else 
162     {
163         dentry = kern_path_create(AT_FDCWD, pathname, &tmp_path, 1);
164         
165         if (!dentry || IS_ERR(dentry)) {
166             return 0;
167         }
168         
169         path_ptr = &tmp_path;
170     }
171 #endif    
172
173
174     if (!(!dentry || IS_ERR(dentry))) {
175         ret = vfs_mkdir(path_ptr->dentry->d_inode, dentry, perms);
176     }
177
178     mutex_unlock(&(path_ptr->dentry->d_inode->i_mutex));
179     path_put(path_ptr);
180
181     return ret;
182 }
183
184
185 static void * palacios_file_open(const char * path, int mode, void * private_data) {
186     struct v3_guest * guest = (struct v3_guest *)private_data;
187     struct palacios_file * pfile = NULL;        
188     struct vm_file_state * vm_state = NULL;
189
190     if (guest != NULL) {
191         vm_state = get_vm_ext_data(guest, "FILE_INTERFACE");
192         
193         if (vm_state == NULL) {
194             ERROR("ERROR: Could not locate vm file state for extension FILE_INTERFACE\n");
195             return NULL;
196         }
197     }
198     
199     pfile = palacios_alloc(sizeof(struct palacios_file));
200     if (!pfile) { 
201         ERROR("Cannot allocate in file open\n");
202         return NULL;
203     }
204     memset(pfile, 0, sizeof(struct palacios_file));
205
206     if ((mode & FILE_OPEN_MODE_READ) && (mode & FILE_OPEN_MODE_WRITE)) { 
207         pfile->mode = O_RDWR;
208     } else if (mode & FILE_OPEN_MODE_READ) { 
209         pfile->mode = O_RDONLY;
210     } else if (mode & FILE_OPEN_MODE_WRITE) { 
211         pfile->mode = O_WRONLY;
212     } 
213     
214     if (mode & FILE_OPEN_MODE_CREATE) {
215         pfile->mode |= O_CREAT;
216     }
217
218
219     pfile->mode |= O_LARGEFILE;
220
221
222     pfile->filp = filp_open(path, pfile->mode, 0600);  // rw------- to start
223     
224     if (!pfile->filp || IS_ERR(pfile->filp)) {
225         ERROR("Cannot open file: %s\n", path);
226         palacios_free(pfile);
227         return NULL;
228     }
229
230     pfile->path = palacios_alloc(strlen(path) + 1);
231     
232     if (!pfile->path) { 
233         ERROR("Cannot allocate in file open\n");
234         filp_close(pfile->filp,NULL);
235         palacios_free(pfile);
236         return NULL;
237     }
238     strncpy(pfile->path, path, strlen(path)); // will terminate pfile->path
239     pfile->guest = guest;
240     
241     palacios_spinlock_init(&(pfile->lock));
242
243     if (guest == NULL) {
244         list_add(&(pfile->file_node), &(global_files));
245     } else {
246         list_add(&(pfile->file_node), &(vm_state->open_files));
247     } 
248
249
250     return pfile;
251 }
252
253 static int palacios_file_close(void * file_ptr) {
254     struct palacios_file * pfile = (struct palacios_file *)file_ptr;
255
256     if (!pfile) {
257         return -1;
258     }
259
260     filp_close(pfile->filp, NULL);
261     
262     list_del(&(pfile->file_node));
263
264     palacios_spinlock_deinit(&(pfile->lock));
265
266     palacios_free(pfile->path);    
267     palacios_free(pfile);
268
269     return 0;
270 }
271
272 static unsigned long long palacios_file_size(void * file_ptr) {
273     struct palacios_file * pfile = (struct palacios_file *)file_ptr;
274     struct file * filp = pfile->filp;
275     struct kstat s;
276     int ret;
277     
278     ret = vfs_getattr(filp->f_path.mnt, filp->f_path.dentry, &s);
279
280     if (ret != 0) {
281         ERROR("Failed to fstat file\n");
282         return -1;
283     }
284
285     return s.size;
286 }
287
288 static unsigned long long palacios_file_read(void * file_ptr, void * buffer, unsigned long long length, unsigned long long offset){
289     struct palacios_file * pfile = (struct palacios_file *)file_ptr;
290     struct file * filp = pfile->filp;
291     ssize_t ret;
292     mm_segment_t old_fs;
293         
294     old_fs = get_fs();
295     set_fs(get_ds());
296         
297     ret = vfs_read(filp, buffer, length, &offset);
298         
299     set_fs(old_fs);
300         
301     if (ret <= 0) {
302         ERROR("sys_read of %p for %lld bytes at offset %llu failed (ret=%ld)\n", filp, length, offset, ret);
303     }
304         
305     return ret;
306 }
307
308
309 static unsigned long long palacios_file_write(void * file_ptr, void * buffer, unsigned long long length, unsigned long long offset) {
310     struct palacios_file * pfile = (struct palacios_file *)file_ptr;
311     struct file * filp = pfile->filp;
312     mm_segment_t old_fs;
313     ssize_t ret;
314
315     old_fs = get_fs();
316     set_fs(get_ds());
317
318     ret = vfs_write(filp, buffer, length, &offset);
319         
320     set_fs(old_fs);
321
322  
323     if (ret <= 0) {
324         ERROR("sys_write for %llu bytes at offset %llu failed (ret=%ld)\n", length, offset, ret);
325     }
326         
327     return ret;
328 }
329
330
331 static struct v3_file_hooks palacios_file_hooks = {
332         .open           = palacios_file_open,
333         .close          = palacios_file_close,
334         .read           = palacios_file_read,
335         .write          = palacios_file_write,
336         .size           = palacios_file_size,
337         .mkdir          = palacios_file_mkdir,
338 };
339
340
341
342 static int file_init( void ) {
343     INIT_LIST_HEAD(&(global_files));
344
345     V3_Init_File(&palacios_file_hooks);
346
347     return 0;
348 }
349
350
351 static int file_deinit( void ) {
352     struct palacios_file * pfile = NULL;
353     struct palacios_file * tmp = NULL;
354     
355     list_for_each_entry_safe(pfile, tmp, &(global_files), file_node) { 
356         filp_close(pfile->filp, NULL);
357         list_del(&(pfile->file_node));
358         palacios_free(pfile->path);    
359         palacios_free(pfile);
360     }
361
362     return 0;
363 }
364
365 static int guest_file_init(struct v3_guest * guest, void ** vm_data) {
366     struct vm_file_state * state = palacios_alloc(sizeof(struct vm_file_state));
367
368     if (!state) {
369         ERROR("Cannot allocate when intializing file services for guest\n");
370         return -1;
371     }
372         
373     
374     INIT_LIST_HEAD(&(state->open_files));
375
376     *vm_data = state;
377
378
379     return 0;
380 }
381
382
383 static int guest_file_deinit(struct v3_guest * guest, void * vm_data) {
384     struct vm_file_state * state = (struct vm_file_state *)vm_data;
385     struct palacios_file * pfile = NULL;
386     struct palacios_file * tmp = NULL;
387     
388     list_for_each_entry_safe(pfile, tmp, &(state->open_files), file_node) { 
389         filp_close(pfile->filp, NULL);
390         list_del(&(pfile->file_node));
391         palacios_free(pfile->path);    
392         palacios_free(pfile);
393     }
394
395     palacios_free(state);
396     return 0;
397 }
398
399
400 static struct linux_ext file_ext = {
401     .name = "FILE_INTERFACE",
402     .init = file_init, 
403     .deinit = file_deinit,
404     .guest_init = guest_file_init,
405     .guest_deinit = guest_file_deinit
406 };
407
408
409 register_extension(&file_ext);