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