~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/vboxsf/utils.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: MIT
  2 /*
  3  * VirtualBox Guest Shared Folders support: Utility functions.
  4  * Mainly conversion from/to VirtualBox/Linux data structures.
  5  *
  6  * Copyright (C) 2006-2018 Oracle Corporation
  7  */
  8 
  9 #include <linux/namei.h>
 10 #include <linux/nls.h>
 11 #include <linux/sizes.h>
 12 #include <linux/pagemap.h>
 13 #include <linux/vfs.h>
 14 #include "vfsmod.h"
 15 
 16 struct inode *vboxsf_new_inode(struct super_block *sb)
 17 {
 18         struct vboxsf_sbi *sbi = VBOXSF_SBI(sb);
 19         struct inode *inode;
 20         unsigned long flags;
 21         int cursor, ret;
 22         u32 gen;
 23 
 24         inode = new_inode(sb);
 25         if (!inode)
 26                 return ERR_PTR(-ENOMEM);
 27 
 28         idr_preload(GFP_KERNEL);
 29         spin_lock_irqsave(&sbi->ino_idr_lock, flags);
 30         cursor = idr_get_cursor(&sbi->ino_idr);
 31         ret = idr_alloc_cyclic(&sbi->ino_idr, inode, 1, 0, GFP_ATOMIC);
 32         if (ret >= 0 && ret < cursor)
 33                 sbi->next_generation++;
 34         gen = sbi->next_generation;
 35         spin_unlock_irqrestore(&sbi->ino_idr_lock, flags);
 36         idr_preload_end();
 37 
 38         if (ret < 0) {
 39                 iput(inode);
 40                 return ERR_PTR(ret);
 41         }
 42 
 43         inode->i_ino = ret;
 44         inode->i_generation = gen;
 45         return inode;
 46 }
 47 
 48 /* set [inode] attributes based on [info], uid/gid based on [sbi] */
 49 int vboxsf_init_inode(struct vboxsf_sbi *sbi, struct inode *inode,
 50                        const struct shfl_fsobjinfo *info, bool reinit)
 51 {
 52         const struct shfl_fsobjattr *attr;
 53         s64 allocated;
 54         umode_t mode;
 55 
 56         attr = &info->attr;
 57 
 58 #define mode_set(r) ((attr->mode & (SHFL_UNIX_##r)) ? (S_##r) : 0)
 59 
 60         mode = mode_set(IRUSR);
 61         mode |= mode_set(IWUSR);
 62         mode |= mode_set(IXUSR);
 63 
 64         mode |= mode_set(IRGRP);
 65         mode |= mode_set(IWGRP);
 66         mode |= mode_set(IXGRP);
 67 
 68         mode |= mode_set(IROTH);
 69         mode |= mode_set(IWOTH);
 70         mode |= mode_set(IXOTH);
 71 
 72 #undef mode_set
 73 
 74         /* We use the host-side values for these */
 75         inode->i_flags |= S_NOATIME | S_NOCMTIME;
 76         inode->i_mapping->a_ops = &vboxsf_reg_aops;
 77 
 78         if (SHFL_IS_DIRECTORY(attr->mode)) {
 79                 if (sbi->o.dmode_set)
 80                         mode = sbi->o.dmode;
 81                 mode &= ~sbi->o.dmask;
 82                 mode |= S_IFDIR;
 83                 if (!reinit) {
 84                         inode->i_op = &vboxsf_dir_iops;
 85                         inode->i_fop = &vboxsf_dir_fops;
 86                         /*
 87                          * XXX: this probably should be set to the number of entries
 88                          * in the directory plus two (. ..)
 89                          */
 90                         set_nlink(inode, 1);
 91                 } else if (!S_ISDIR(inode->i_mode))
 92                         return -ESTALE;
 93                 inode->i_mode = mode;
 94         } else if (SHFL_IS_SYMLINK(attr->mode)) {
 95                 if (sbi->o.fmode_set)
 96                         mode = sbi->o.fmode;
 97                 mode &= ~sbi->o.fmask;
 98                 mode |= S_IFLNK;
 99                 if (!reinit) {
100                         inode->i_op = &vboxsf_lnk_iops;
101                         set_nlink(inode, 1);
102                 } else if (!S_ISLNK(inode->i_mode))
103                         return -ESTALE;
104                 inode->i_mode = mode;
105         } else {
106                 if (sbi->o.fmode_set)
107                         mode = sbi->o.fmode;
108                 mode &= ~sbi->o.fmask;
109                 mode |= S_IFREG;
110                 if (!reinit) {
111                         inode->i_op = &vboxsf_reg_iops;
112                         inode->i_fop = &vboxsf_reg_fops;
113                         set_nlink(inode, 1);
114                 } else if (!S_ISREG(inode->i_mode))
115                         return -ESTALE;
116                 inode->i_mode = mode;
117         }
118 
119         inode->i_uid = sbi->o.uid;
120         inode->i_gid = sbi->o.gid;
121 
122         inode->i_size = info->size;
123         inode->i_blkbits = 12;
124         /* i_blocks always in units of 512 bytes! */
125         allocated = info->allocated + 511;
126         do_div(allocated, 512);
127         inode->i_blocks = allocated;
128 
129         inode_set_atime_to_ts(inode,
130                               ns_to_timespec64(info->access_time.ns_relative_to_unix_epoch));
131         inode_set_ctime_to_ts(inode,
132                               ns_to_timespec64(info->change_time.ns_relative_to_unix_epoch));
133         inode_set_mtime_to_ts(inode,
134                               ns_to_timespec64(info->modification_time.ns_relative_to_unix_epoch));
135         return 0;
136 }
137 
138 int vboxsf_create_at_dentry(struct dentry *dentry,
139                             struct shfl_createparms *params)
140 {
141         struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb);
142         struct shfl_string *path;
143         int err;
144 
145         path = vboxsf_path_from_dentry(sbi, dentry);
146         if (IS_ERR(path))
147                 return PTR_ERR(path);
148 
149         err = vboxsf_create(sbi->root, path, params);
150         __putname(path);
151 
152         return err;
153 }
154 
155 int vboxsf_stat(struct vboxsf_sbi *sbi, struct shfl_string *path,
156                 struct shfl_fsobjinfo *info)
157 {
158         struct shfl_createparms params = {};
159         int err;
160 
161         params.handle = SHFL_HANDLE_NIL;
162         params.create_flags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;
163 
164         err = vboxsf_create(sbi->root, path, &params);
165         if (err)
166                 return err;
167 
168         if (params.result != SHFL_FILE_EXISTS)
169                 return -ENOENT;
170 
171         if (info)
172                 *info = params.info;
173 
174         return 0;
175 }
176 
177 int vboxsf_stat_dentry(struct dentry *dentry, struct shfl_fsobjinfo *info)
178 {
179         struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb);
180         struct shfl_string *path;
181         int err;
182 
183         path = vboxsf_path_from_dentry(sbi, dentry);
184         if (IS_ERR(path))
185                 return PTR_ERR(path);
186 
187         err = vboxsf_stat(sbi, path, info);
188         __putname(path);
189         return err;
190 }
191 
192 int vboxsf_inode_revalidate(struct dentry *dentry)
193 {
194         struct vboxsf_sbi *sbi;
195         struct vboxsf_inode *sf_i;
196         struct shfl_fsobjinfo info;
197         struct timespec64 mtime, prev_mtime;
198         struct inode *inode;
199         int err;
200 
201         if (!dentry || !d_really_is_positive(dentry))
202                 return -EINVAL;
203 
204         inode = d_inode(dentry);
205         prev_mtime = inode_get_mtime(inode);
206         sf_i = VBOXSF_I(inode);
207         sbi = VBOXSF_SBI(dentry->d_sb);
208         if (!sf_i->force_restat) {
209                 if (time_before(jiffies, dentry->d_time + sbi->o.ttl))
210                         return 0;
211         }
212 
213         err = vboxsf_stat_dentry(dentry, &info);
214         if (err)
215                 return err;
216 
217         dentry->d_time = jiffies;
218         sf_i->force_restat = 0;
219         err = vboxsf_init_inode(sbi, inode, &info, true);
220         if (err)
221                 return err;
222 
223         /*
224          * If the file was changed on the host side we need to invalidate the
225          * page-cache for it.  Note this also gets triggered by our own writes,
226          * this is unavoidable.
227          */
228         mtime = inode_get_mtime(inode);
229         if (timespec64_compare(&mtime, &prev_mtime) > 0)
230                 invalidate_inode_pages2(inode->i_mapping);
231 
232         return 0;
233 }
234 
235 int vboxsf_getattr(struct mnt_idmap *idmap, const struct path *path,
236                    struct kstat *kstat, u32 request_mask, unsigned int flags)
237 {
238         int err;
239         struct dentry *dentry = path->dentry;
240         struct inode *inode = d_inode(dentry);
241         struct vboxsf_inode *sf_i = VBOXSF_I(inode);
242 
243         switch (flags & AT_STATX_SYNC_TYPE) {
244         case AT_STATX_DONT_SYNC:
245                 err = 0;
246                 break;
247         case AT_STATX_FORCE_SYNC:
248                 sf_i->force_restat = 1;
249                 fallthrough;
250         default:
251                 err = vboxsf_inode_revalidate(dentry);
252         }
253         if (err)
254                 return err;
255 
256         generic_fillattr(&nop_mnt_idmap, request_mask, d_inode(dentry), kstat);
257         return 0;
258 }
259 
260 int vboxsf_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
261                    struct iattr *iattr)
262 {
263         struct vboxsf_inode *sf_i = VBOXSF_I(d_inode(dentry));
264         struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb);
265         struct shfl_createparms params = {};
266         struct shfl_fsobjinfo info = {};
267         u32 buf_len;
268         int err;
269 
270         params.handle = SHFL_HANDLE_NIL;
271         params.create_flags = SHFL_CF_ACT_OPEN_IF_EXISTS |
272                               SHFL_CF_ACT_FAIL_IF_NEW |
273                               SHFL_CF_ACCESS_ATTR_WRITE;
274 
275         /* this is at least required for Posix hosts */
276         if (iattr->ia_valid & ATTR_SIZE)
277                 params.create_flags |= SHFL_CF_ACCESS_WRITE;
278 
279         err = vboxsf_create_at_dentry(dentry, &params);
280         if (err || params.result != SHFL_FILE_EXISTS)
281                 return err ? err : -ENOENT;
282 
283 #define mode_set(r) ((iattr->ia_mode & (S_##r)) ? SHFL_UNIX_##r : 0)
284 
285         /*
286          * Setting the file size and setting the other attributes has to
287          * be handled separately.
288          */
289         if (iattr->ia_valid & (ATTR_MODE | ATTR_ATIME | ATTR_MTIME)) {
290                 if (iattr->ia_valid & ATTR_MODE) {
291                         info.attr.mode = mode_set(IRUSR);
292                         info.attr.mode |= mode_set(IWUSR);
293                         info.attr.mode |= mode_set(IXUSR);
294                         info.attr.mode |= mode_set(IRGRP);
295                         info.attr.mode |= mode_set(IWGRP);
296                         info.attr.mode |= mode_set(IXGRP);
297                         info.attr.mode |= mode_set(IROTH);
298                         info.attr.mode |= mode_set(IWOTH);
299                         info.attr.mode |= mode_set(IXOTH);
300 
301                         if (iattr->ia_mode & S_IFDIR)
302                                 info.attr.mode |= SHFL_TYPE_DIRECTORY;
303                         else
304                                 info.attr.mode |= SHFL_TYPE_FILE;
305                 }
306 
307                 if (iattr->ia_valid & ATTR_ATIME)
308                         info.access_time.ns_relative_to_unix_epoch =
309                                             timespec64_to_ns(&iattr->ia_atime);
310 
311                 if (iattr->ia_valid & ATTR_MTIME)
312                         info.modification_time.ns_relative_to_unix_epoch =
313                                             timespec64_to_ns(&iattr->ia_mtime);
314 
315                 /*
316                  * Ignore ctime (inode change time) as it can't be set
317                  * from userland anyway.
318                  */
319 
320                 buf_len = sizeof(info);
321                 err = vboxsf_fsinfo(sbi->root, params.handle,
322                                     SHFL_INFO_SET | SHFL_INFO_FILE, &buf_len,
323                                     &info);
324                 if (err) {
325                         vboxsf_close(sbi->root, params.handle);
326                         return err;
327                 }
328 
329                 /* the host may have given us different attr then requested */
330                 sf_i->force_restat = 1;
331         }
332 
333 #undef mode_set
334 
335         if (iattr->ia_valid & ATTR_SIZE) {
336                 memset(&info, 0, sizeof(info));
337                 info.size = iattr->ia_size;
338                 buf_len = sizeof(info);
339                 err = vboxsf_fsinfo(sbi->root, params.handle,
340                                     SHFL_INFO_SET | SHFL_INFO_SIZE, &buf_len,
341                                     &info);
342                 if (err) {
343                         vboxsf_close(sbi->root, params.handle);
344                         return err;
345                 }
346 
347                 /* the host may have given us different attr then requested */
348                 sf_i->force_restat = 1;
349         }
350 
351         vboxsf_close(sbi->root, params.handle);
352 
353         /* Update the inode with what the host has actually given us. */
354         if (sf_i->force_restat)
355                 vboxsf_inode_revalidate(dentry);
356 
357         return 0;
358 }
359 
360 /*
361  * [dentry] contains string encoded in coding system that corresponds
362  * to [sbi]->nls, we must convert it to UTF8 here.
363  * Returns a shfl_string allocated through __getname (must be freed using
364  * __putname), or an ERR_PTR on error.
365  */
366 struct shfl_string *vboxsf_path_from_dentry(struct vboxsf_sbi *sbi,
367                                             struct dentry *dentry)
368 {
369         struct shfl_string *shfl_path;
370         int path_len, out_len, nb;
371         char *buf, *path;
372         wchar_t uni;
373         u8 *out;
374 
375         buf = __getname();
376         if (!buf)
377                 return ERR_PTR(-ENOMEM);
378 
379         path = dentry_path_raw(dentry, buf, PATH_MAX);
380         if (IS_ERR(path)) {
381                 __putname(buf);
382                 return ERR_CAST(path);
383         }
384         path_len = strlen(path);
385 
386         if (sbi->nls) {
387                 shfl_path = __getname();
388                 if (!shfl_path) {
389                         __putname(buf);
390                         return ERR_PTR(-ENOMEM);
391                 }
392 
393                 out = shfl_path->string.utf8;
394                 out_len = PATH_MAX - SHFLSTRING_HEADER_SIZE - 1;
395 
396                 while (path_len) {
397                         nb = sbi->nls->char2uni(path, path_len, &uni);
398                         if (nb < 0) {
399                                 __putname(shfl_path);
400                                 __putname(buf);
401                                 return ERR_PTR(-EINVAL);
402                         }
403                         path += nb;
404                         path_len -= nb;
405 
406                         nb = utf32_to_utf8(uni, out, out_len);
407                         if (nb < 0) {
408                                 __putname(shfl_path);
409                                 __putname(buf);
410                                 return ERR_PTR(-ENAMETOOLONG);
411                         }
412                         out += nb;
413                         out_len -= nb;
414                 }
415                 *out = 0;
416                 shfl_path->length = out - shfl_path->string.utf8;
417                 shfl_path->size = shfl_path->length + 1;
418                 __putname(buf);
419         } else {
420                 if ((SHFLSTRING_HEADER_SIZE + path_len + 1) > PATH_MAX) {
421                         __putname(buf);
422                         return ERR_PTR(-ENAMETOOLONG);
423                 }
424                 /*
425                  * dentry_path stores the name at the end of buf, but the
426                  * shfl_string string we return must be properly aligned.
427                  */
428                 shfl_path = (struct shfl_string *)buf;
429                 memmove(shfl_path->string.utf8, path, path_len);
430                 shfl_path->string.utf8[path_len] = 0;
431                 shfl_path->length = path_len;
432                 shfl_path->size = path_len + 1;
433         }
434 
435         return shfl_path;
436 }
437 
438 int vboxsf_nlscpy(struct vboxsf_sbi *sbi, char *name, size_t name_bound_len,
439                   const unsigned char *utf8_name, size_t utf8_len)
440 {
441         const char *in;
442         char *out;
443         size_t out_bound_len;
444         size_t in_bound_len;
445 
446         in = utf8_name;
447         in_bound_len = utf8_len;
448 
449         out = name;
450         /* Reserve space for terminating 0 */
451         out_bound_len = name_bound_len - 1;
452 
453         while (in_bound_len) {
454                 int nb;
455                 unicode_t uni;
456 
457                 nb = utf8_to_utf32(in, in_bound_len, &uni);
458                 if (nb < 0)
459                         return -EINVAL;
460 
461                 in += nb;
462                 in_bound_len -= nb;
463 
464                 nb = sbi->nls->uni2char(uni, out, out_bound_len);
465                 if (nb < 0)
466                         return nb;
467 
468                 out += nb;
469                 out_bound_len -= nb;
470         }
471 
472         *out = 0;
473 
474         return 0;
475 }
476 
477 static struct vboxsf_dir_buf *vboxsf_dir_buf_alloc(struct list_head *list)
478 {
479         struct vboxsf_dir_buf *b;
480 
481         b = kmalloc(sizeof(*b), GFP_KERNEL);
482         if (!b)
483                 return NULL;
484 
485         b->buf = kmalloc(DIR_BUFFER_SIZE, GFP_KERNEL);
486         if (!b->buf) {
487                 kfree(b);
488                 return NULL;
489         }
490 
491         b->entries = 0;
492         b->used = 0;
493         b->free = DIR_BUFFER_SIZE;
494         list_add(&b->head, list);
495 
496         return b;
497 }
498 
499 static void vboxsf_dir_buf_free(struct vboxsf_dir_buf *b)
500 {
501         list_del(&b->head);
502         kfree(b->buf);
503         kfree(b);
504 }
505 
506 struct vboxsf_dir_info *vboxsf_dir_info_alloc(void)
507 {
508         struct vboxsf_dir_info *p;
509 
510         p = kmalloc(sizeof(*p), GFP_KERNEL);
511         if (!p)
512                 return NULL;
513 
514         INIT_LIST_HEAD(&p->info_list);
515         return p;
516 }
517 
518 void vboxsf_dir_info_free(struct vboxsf_dir_info *p)
519 {
520         struct list_head *list, *pos, *tmp;
521 
522         list = &p->info_list;
523         list_for_each_safe(pos, tmp, list) {
524                 struct vboxsf_dir_buf *b;
525 
526                 b = list_entry(pos, struct vboxsf_dir_buf, head);
527                 vboxsf_dir_buf_free(b);
528         }
529         kfree(p);
530 }
531 
532 int vboxsf_dir_read_all(struct vboxsf_sbi *sbi, struct vboxsf_dir_info *sf_d,
533                         u64 handle)
534 {
535         struct vboxsf_dir_buf *b;
536         u32 entries, size;
537         int err = 0;
538         void *buf;
539 
540         /* vboxsf_dirinfo returns 1 on end of dir */
541         while (err == 0) {
542                 b = vboxsf_dir_buf_alloc(&sf_d->info_list);
543                 if (!b) {
544                         err = -ENOMEM;
545                         break;
546                 }
547 
548                 buf = b->buf;
549                 size = b->free;
550 
551                 err = vboxsf_dirinfo(sbi->root, handle, NULL, 0, 0,
552                                      &size, buf, &entries);
553                 if (err < 0)
554                         break;
555 
556                 b->entries += entries;
557                 b->free -= size;
558                 b->used += size;
559         }
560 
561         if (b && b->used == 0)
562                 vboxsf_dir_buf_free(b);
563 
564         /* -EILSEQ means the host could not translate a filename, ignore */
565         if (err > 0 || err == -EILSEQ)
566                 err = 0;
567 
568         return err;
569 }
570 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php