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.


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