text: /** @file
   *
   * vboxsf -- VirtualBox Guest Additions for Linux:
   * Virtual File System for VirtualBox Shared Folders
   *
   * Module initialization/finalization
   * File system registration/deregistration
   * Superblock reading
   * Few utility functions
   */
  
  /*
   * Copyright (C) 2006-2018 Oracle Corporation
   *
   * Permission is hereby granted, free of charge, to any person
   * obtaining a copy of this software and associated documentation
   * files (the "Software"), to deal in the Software without
   * restriction, including without limitation the rights to use,
   * copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following
   * conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
   * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
   * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   * OTHER DEALINGS IN THE SOFTWARE.
   */
  
  /**
   * @note Anyone wishing to make changes here might wish to take a look at
   *  https://github.com/torvalds/linux/blob/master/Documentation/filesystems/vfs.txt
   * which seems to be the closest there is to official documentation on
   * writing filesystem drivers for Linux.
   */
  
  #include "vfsmod.h"
  #include "version-generated.h"
  #include "revision-generated.h"
  #include "product-generated.h"
  #include "VBoxGuestR0LibInternal.h"
  
  MODULE_DESCRIPTION(VBOX_PRODUCT " VFS Module for Host File System Access");
  MODULE_AUTHOR(VBOX_VENDOR);
  MODULE_LICENSE("GPL and additional rights");
  #ifdef MODULE_ALIAS_FS
  MODULE_ALIAS_FS("vboxsf");
  #endif
  #ifdef MODULE_VERSION
  MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV));
  #endif
  
  /* globals */
  VBGLSFCLIENT client_handle;
  
  /* forward declarations */
  static struct super_operations sf_super_ops;
  
  /* allocate global info, try to map host share */
  static int sf_glob_alloc(struct vbsf_mount_info_new *info,
  			 struct sf_glob_info **sf_gp)
  {
  	int err, rc;
  	SHFLSTRING *str_name;
  	size_t name_len, str_len;
  	struct sf_glob_info *sf_g;
  
  	TRACE();
  	sf_g = kmalloc(sizeof(*sf_g), GFP_KERNEL);
  	if (!sf_g) {
  		err = -ENOMEM;
  		LogRelFunc(("could not allocate memory for global info\n"));
  		goto fail0;
  	}
  
  	RT_ZERO(*sf_g);
  
  	if (info->nullchar != '\0'
  	    || info->signature[0] != VBSF_MOUNT_SIGNATURE_BYTE_0
  	    || info->signature[1] != VBSF_MOUNT_SIGNATURE_BYTE_1
  	    || info->signature[2] != VBSF_MOUNT_SIGNATURE_BYTE_2) {
  		err = -EINVAL;
  		goto fail1;
  	}
  
  	info->name[sizeof(info->name) - 1] = 0;
  	info->nls_name[sizeof(info->nls_name) - 1] = 0;
  
  	name_len = strlen(info->name);
  	str_len = offsetof(SHFLSTRING, String.utf8) + name_len + 1;
  	str_name = kmalloc(str_len, GFP_KERNEL);
  	if (!str_name) {
  		err = -ENOMEM;
  		LogRelFunc(("could not allocate memory for host name\n"));
  		goto fail1;
  	}
  
  	str_name->u16Length = name_len;
  	str_name->u16Size = name_len + 1;
  	memcpy(str_name->String.utf8, info->name, name_len + 1);
  
  #define _IS_UTF8(_str) \
      (strcmp(_str, "utf8") == 0)
  #define _IS_EMPTY(_str) \
      (strcmp(_str, "") == 0)
  
  	/* Check if NLS charset is valid and not points to UTF8 table */
  	if (info->nls_name[0]) {
  		if (_IS_UTF8(info->nls_name))
  			sf_g->nls = NULL;
  		else {
  			sf_g->nls = load_nls(info->nls_name);
  			if (!sf_g->nls) {
  				err = -EINVAL;
  				LogFunc(("failed to load nls %s\n",
  					 info->nls_name));
  				kfree(str_name);
  				goto fail1;
  			}
  		}
  	} else {
  #ifdef CONFIG_NLS_DEFAULT
  		/* If no NLS charset specified, try to load the default
  		 * one if it's not points to UTF8. */
  		if (!_IS_UTF8(CONFIG_NLS_DEFAULT)
  		    && !_IS_EMPTY(CONFIG_NLS_DEFAULT))
  			sf_g->nls = load_nls_default();
  		else
  			sf_g->nls = NULL;
  #else
  		sf_g->nls = NULL;
  #endif
  
  #undef _IS_UTF8
  #undef _IS_EMPTY
  	}
  
  	rc = VbglR0SfMapFolder(&client_handle, str_name, &sf_g->map);
  	kfree(str_name);
  
  	if (RT_FAILURE(rc)) {
  		err = -EPROTO;
  		LogFunc(("VbglR0SfMapFolder failed rc=%d\n", rc));
  		goto fail2;
  	}
  
  	sf_g->ttl = info->ttl;
  	sf_g->uid = info->uid;
  	sf_g->gid = info->gid;
  
  	if ((unsigned)info->length >= sizeof(struct vbsf_mount_info_new)) {
  		/* new fields */
  		sf_g->dmode = info->dmode;
  		sf_g->fmode = info->fmode;
  		sf_g->dmask = info->dmask;
  		sf_g->fmask = info->fmask;
  	} else {
  		sf_g->dmode = ~0;
  		sf_g->fmode = ~0;
  	}
  
  	*sf_gp = sf_g;
  	return 0;
  
   fail2:
  	if (sf_g->nls)
  		unload_nls(sf_g->nls);
  
   fail1:
  	kfree(sf_g);
  
   fail0:
  	return err;
  }
  
  /* unmap the share and free global info [sf_g] */
  static void sf_glob_free(struct sf_glob_info *sf_g)
  {
  	int rc;
  
  	TRACE();
  	rc = VbglR0SfUnmapFolder(&client_handle, &sf_g->map);
  	if (RT_FAILURE(rc))
  		LogFunc(("VbglR0SfUnmapFolder failed rc=%d\n", rc));
  
  	if (sf_g->nls)
  		unload_nls(sf_g->nls);
  
  	kfree(sf_g);
  }
  
  /**
   * This is called (by sf_read_super_[24|26] when vfs mounts the fs and
   * wants to read super_block.
   *
   * calls [sf_glob_alloc] to map the folder and allocate global
   * information structure.
   *
   * initializes [sb], initializes root inode and dentry.
   *
   * should respect [flags]
   */
  static int sf_read_super_aux(struct super_block *sb, void *data, int flags)
  {
  	int err;
  	struct dentry *droot;
  	struct inode *iroot;
  	struct sf_inode_info *sf_i;
  	struct sf_glob_info *sf_g;
  	SHFLFSOBJINFO fsinfo;
  	struct vbsf_mount_info_new *info;
  	bool fInodePut = true;
  
  	TRACE();
  	if (!data) {
  		LogFunc(("no mount info specified\n"));
  		return -EINVAL;
  	}
  
  	info = data;
  
  	if (flags & MS_REMOUNT) {
  		LogFunc(("remounting is not supported\n"));
  		return -ENOSYS;
  	}
  
  	err = sf_glob_alloc(info, &sf_g);
  	if (err)
  		goto fail0;
  
  	sf_i = kmalloc(sizeof(*sf_i), GFP_KERNEL);
  	if (!sf_i) {
  		err = -ENOMEM;
  		LogRelFunc(("could not allocate memory for root inode info\n"));
  		goto fail1;
  	}
  
  	sf_i->handle = SHFL_HANDLE_NIL;
  	sf_i->path = kmalloc(sizeof(SHFLSTRING) + 1, GFP_KERNEL);
  	if (!sf_i->path) {
  		err = -ENOMEM;
  		LogRelFunc(("could not allocate memory for root inode path\n"));
  		goto fail2;
  	}
  
  	sf_i->path->u16Length = 1;
  	sf_i->path->u16Size = 2;
  	sf_i->path->String.utf8[0] = '/';
  	sf_i->path->String.utf8[1] = 0;
  	sf_i->force_reread = 0;
  
  	err = sf_stat(__func__, sf_g, sf_i->path, &fsinfo, 0);
  	if (err) {
  		LogFunc(("could not stat root of share\n"));
  		goto fail3;
  	}
  
  	sb->s_magic = 0xface;
  	sb->s_blocksize = 1024;
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 3)
  	/* Required for seek/sendfile.
  	 *
  	 * Must by less than or equal to INT64_MAX despite the fact that the
  	 * declaration of this variable is unsigned long long. See determination
  	 * of 'loff_t max' in fs/read_write.c / do_sendfile(). I don't know the
  	 * correct limit but MAX_LFS_FILESIZE (8TB-1 on 32-bit boxes) takes the
  	 * page cache into account and is the suggested limit. */
  #if defined MAX_LFS_FILESIZE
  	sb->s_maxbytes = MAX_LFS_FILESIZE;
  #else
  	sb->s_maxbytes = 0x7fffffffffffffffULL;
  #endif
  #endif
  	sb->s_op = &sf_super_ops;
  
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
  	iroot = iget_locked(sb, 0);
  #else
  	iroot = iget(sb, 0);
  #endif
  	if (!iroot) {
  		err = -ENOMEM;	/* XXX */
  		LogFunc(("could not get root inode\n"));
  		goto fail3;
  	}
  
  	if (sf_init_backing_dev(sf_g)) {
  		err = -EINVAL;
  		LogFunc(("could not init bdi\n"));
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
  		unlock_new_inode(iroot);
  #endif
  		goto fail4;
  	}
  
  	sf_init_inode(sf_g, iroot, &fsinfo);
  	SET_INODE_INFO(iroot, sf_i);
  
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
  	unlock_new_inode(iroot);
  #endif
  
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
  	droot = d_make_root(iroot);
  #else
  	droot = d_alloc_root(iroot);
  #endif
  	if (!droot) {
  		err = -ENOMEM;	/* XXX */
  		LogFunc(("d_alloc_root failed\n"));
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
  		fInodePut = false;
  #endif
  		goto fail5;
  	}
  
  	sb->s_root = droot;
  	SET_GLOB_INFO(sb, sf_g);
  	return 0;
  
   fail5:
  	sf_done_backing_dev(sf_g);
  
   fail4:
  	if (fInodePut)
  		iput(iroot);
  
   fail3:
  	kfree(sf_i->path);
  
   fail2:
  	kfree(sf_i);
  
   fail1:
  	sf_glob_free(sf_g);
  
   fail0:
  	return err;
  }
  
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
  static struct super_block *sf_read_super_24(struct super_block *sb, void *data,
  					    int flags)
  {
  	int err;
  
  	TRACE();
  	err = sf_read_super_aux(sb, data, flags);
  	if (err)
  		return NULL;
  
  	return sb;
  }
  #endif
  
  /* this is called when vfs is about to destroy the [inode]. all
     resources associated with this [inode] must be cleared here */
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
  static void sf_clear_inode(struct inode *inode)
  {
  	struct sf_inode_info *sf_i;
  
  	TRACE();
  	sf_i = GET_INODE_INFO(inode);
  	if (!sf_i)
  		return;
  
  	BUG_ON(!sf_i->path);
  	kfree(sf_i->path);
  	kfree(sf_i);
  	SET_INODE_INFO(inode, NULL);
  }
  #else
  static void sf_evict_inode(struct inode *inode)
  {
  	struct sf_inode_info *sf_i;
  
  	TRACE();
  	truncate_inode_pages(&inode->i_data, 0);
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
  	clear_inode(inode);
  #else
  	end_writeback(inode);
  #endif
  
  	sf_i = GET_INODE_INFO(inode);
  	if (!sf_i)
  		return;
  
  	BUG_ON(!sf_i->path);
  	kfree(sf_i->path);
  	kfree(sf_i);
  	SET_INODE_INFO(inode, NULL);
  }
  #endif
  
  /* this is called by vfs when it wants to populate [inode] with data.
     the only thing that is known about inode at this point is its index
     hence we can't do anything here, and let lookup/whatever with the
     job to properly fill then [inode] */
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
  static void sf_read_inode(struct inode *inode)
  {
  }
  #endif
  
  /* vfs is done with [sb] (umount called) call [sf_glob_free] to unmap
     the folder and free [sf_g] */
  static void sf_put_super(struct super_block *sb)
  {
  	struct sf_glob_info *sf_g;
  
  	sf_g = GET_GLOB_INFO(sb);
  	BUG_ON(!sf_g);
  	sf_done_backing_dev(sf_g);
  	sf_glob_free(sf_g);
  }
  
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
  static int sf_statfs(struct super_block *sb, STRUCT_STATFS * stat)
  {
  	return sf_get_volume_info(sb, stat);
  }
  #else
  static int sf_statfs(struct dentry *dentry, STRUCT_STATFS * stat)
  {
  	struct super_block *sb = dentry->d_inode->i_sb;
  	return sf_get_volume_info(sb, stat);
  }
  #endif
  
  static int sf_remount_fs(struct super_block *sb, int *flags, char *data)
  {
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 23)
  	struct sf_glob_info *sf_g;
  	struct sf_inode_info *sf_i;
  	struct inode *iroot;
  	SHFLFSOBJINFO fsinfo;
  	int err;
  
  	sf_g = GET_GLOB_INFO(sb);
  	BUG_ON(!sf_g);
  	if (data && data[0] != 0) {
  		struct vbsf_mount_info_new *info =
  		    (struct vbsf_mount_info_new *)data;
  		if (info->signature[0] == VBSF_MOUNT_SIGNATURE_BYTE_0
  		    && info->signature[1] == VBSF_MOUNT_SIGNATURE_BYTE_1
  		    && info->signature[2] == VBSF_MOUNT_SIGNATURE_BYTE_2) {
  			sf_g->uid = info->uid;
  			sf_g->gid = info->gid;
  			sf_g->ttl = info->ttl;
  			sf_g->dmode = info->dmode;
  			sf_g->fmode = info->fmode;
  			sf_g->dmask = info->dmask;
  			sf_g->fmask = info->fmask;
  		}
  	}
  
  	iroot = ilookup(sb, 0);
  	if (!iroot)
  		return -ENOSYS;
  
  	sf_i = GET_INODE_INFO(iroot);
  	err = sf_stat(__func__, sf_g, sf_i->path, &fsinfo, 0);
  	BUG_ON(err != 0);
  	sf_init_inode(sf_g, iroot, &fsinfo);
  	/*unlock_new_inode(iroot); */
  	return 0;
  #else
  	return -ENOSYS;
  #endif
  }
  
  static struct super_operations sf_super_ops = {
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
  	.clear_inode = sf_clear_inode,
  #else
  	.evict_inode = sf_evict_inode,
  #endif
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
  	.read_inode = sf_read_inode,
  #endif
  	.put_super = sf_put_super,
  	.statfs = sf_statfs,
  	.remount_fs = sf_remount_fs
  };
  
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
  static DECLARE_FSTYPE(vboxsf_fs_type, "vboxsf", sf_read_super_24, 0);
  #else
  static int sf_read_super_26(struct super_block *sb, void *data, int flags)
  {
  	int err;
  
  	TRACE();
  	err = sf_read_super_aux(sb, data, flags);
  	if (err)
  		printk(KERN_DEBUG "sf_read_super_aux err=%d\n", err);
  
  	return err;
  }
  
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
  static struct super_block *sf_get_sb(struct file_system_type *fs_type,
  				     int flags, const char *dev_name,
  				     void *data)
  {
  	TRACE();
  	return get_sb_nodev(fs_type, flags, data, sf_read_super_26);
  }
  #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
  static int sf_get_sb(struct file_system_type *fs_type, int flags,
  		     const char *dev_name, void *data, struct vfsmount *mnt)
  {
  	TRACE();
  	return get_sb_nodev(fs_type, flags, data, sf_read_super_26, mnt);
  }
  #else
  static struct dentry *sf_mount(struct file_system_type *fs_type, int flags,
  			       const char *dev_name, void *data)
  {
  	TRACE();
  	return mount_nodev(fs_type, flags, data, sf_read_super_26);
  }
  #endif
  
  static struct file_system_type vboxsf_fs_type = {
  	.owner = THIS_MODULE,
  	.name = "vboxsf",
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
  	.get_sb = sf_get_sb,
  #else
  	.mount = sf_mount,
  #endif
  	.kill_sb = kill_anon_super
  };
  #endif
  
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
  static int follow_symlinks = 0;
  module_param(follow_symlinks, int, 0);
  MODULE_PARM_DESC(follow_symlinks,
  		 "Let host resolve symlinks rather than showing them");
  #endif
  
  /* Module initialization/finalization handlers */
  static int __init init(void)
  {
  	int rcVBox;
  	int rcRet = 0;
  	int err;
  
  	TRACE();
  
  	if (sizeof(struct vbsf_mount_info_new) > PAGE_SIZE) {
  		printk(KERN_ERR
  		       "Mount information structure is too large %lu\n"
  		       "Must be less than or equal to %lu\n",
  		       (unsigned long)sizeof(struct vbsf_mount_info_new),
  		       (unsigned long)PAGE_SIZE);
  		return -EINVAL;
  	}
  
  	err = register_filesystem(&vboxsf_fs_type);
  	if (err) {
  		LogFunc(("register_filesystem err=%d\n", err));
  		return err;
  	}
  
  	rcVBox = VbglR0HGCMInit();
  	if (RT_FAILURE(rcVBox)) {
  		LogRelFunc(("VbglR0HGCMInit failed, rc=%d\n", rcVBox));
  		rcRet = -EPROTO;
  		goto fail0;
  	}
  
  	rcVBox = VbglR0SfConnect(&client_handle);
  	if (RT_FAILURE(rcVBox)) {
  		LogRelFunc(("VbglR0SfConnect failed, rc=%d\n", rcVBox));
  		rcRet = -EPROTO;
  		goto fail1;
  	}
  
  	rcVBox = VbglR0SfSetUtf8(&client_handle);
  	if (RT_FAILURE(rcVBox)) {
  		LogRelFunc(("VbglR0SfSetUtf8 failed, rc=%d\n", rcVBox));
  		rcRet = -EPROTO;
  		goto fail2;
  	}
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
  	if (!follow_symlinks) {
  		rcVBox = VbglR0SfSetSymlinks(&client_handle);
  		if (RT_FAILURE(rcVBox)) {
  			printk(KERN_WARNING
  			       "vboxsf: Host unable to show symlinks, rc=%d\n",
  			       rcVBox);
  		}
  	}
  #endif
  
  	printk(KERN_DEBUG
  	       "vboxsf: Successfully loaded version " VBOX_VERSION_STRING
  	       " (interface " RT_XSTR(VMMDEV_VERSION) ")\n");
  
  	return 0;
  
   fail2:
  	VbglR0SfDisconnect(&client_handle);
  
   fail1:
  	VbglR0HGCMTerminate();
  
   fail0:
  	unregister_filesystem(&vboxsf_fs_type);
  	return rcRet;
  }
  
  static void __exit fini(void)
  {
  	TRACE();
  
  	VbglR0SfDisconnect(&client_handle);
  	VbglR0HGCMTerminate();
  	unregister_filesystem(&vboxsf_fs_type);
  }
  
  module_init(init);
  module_exit(fini);
  
  /* C++ hack */
  int __gxx_personality_v0 = 0xdeadbeef;
  