summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/linux
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Additions/linux
parentInitial commit. (diff)
downloadvirtualbox-upstream.tar.xz
virtualbox-upstream.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/VBox/Additions/linux/Makefile103
-rw-r--r--src/VBox/Additions/linux/Makefile.kmk450
-rw-r--r--src/VBox/Additions/linux/drm/.scm-settings25
-rw-r--r--src/VBox/Additions/linux/drm/Makefile.kmk42
-rw-r--r--src/VBox/Additions/linux/drm/Makefile.module.kms51
-rw-r--r--src/VBox/Additions/linux/drm/README.testing13
-rwxr-xr-xsrc/VBox/Additions/linux/drm/files_vboxvideo_drv49
-rw-r--r--src/VBox/Additions/linux/drm/indent.sed274
-rw-r--r--src/VBox/Additions/linux/drm/vbox_drv.c341
-rw-r--r--src/VBox/Additions/linux/drm/vbox_drv.h425
-rw-r--r--src/VBox/Additions/linux/drm/vbox_fb.c458
-rw-r--r--src/VBox/Additions/linux/drm/vbox_hgsmi.c130
-rw-r--r--src/VBox/Additions/linux/drm/vbox_irq.c212
-rw-r--r--src/VBox/Additions/linux/drm/vbox_main.c661
-rw-r--r--src/VBox/Additions/linux/drm/vbox_mode.c857
-rw-r--r--src/VBox/Additions/linux/drm/vbox_prime.c80
-rw-r--r--src/VBox/Additions/linux/drm/vbox_ttm.c550
-rwxr-xr-xsrc/VBox/Additions/linux/export_modules.sh146
-rw-r--r--src/VBox/Additions/linux/installer/.scm-settings20
-rwxr-xr-xsrc/VBox/Additions/linux/installer/autorun.sh190
-rw-r--r--src/VBox/Additions/linux/installer/deffiles84
-rwxr-xr-xsrc/VBox/Additions/linux/installer/install.sh.in632
-rw-r--r--src/VBox/Additions/linux/installer/module-autologon.sh172
-rwxr-xr-xsrc/VBox/Additions/linux/installer/vboxadd-service.sh159
-rwxr-xr-xsrc/VBox/Additions/linux/installer/vboxadd-x11.sh613
-rwxr-xr-xsrc/VBox/Additions/linux/installer/vboxadd.sh595
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/.scm-settings20
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/Config.kmk31
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/Makefile.kmk104
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/banner-dummy.pngbin0 -> 1622 bytes
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/Makefile.kmk44
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/config.h90
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/greeter.c1442
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/language.c416
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/layout.c344
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/power.c211
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/session.c388
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/system.c42
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/user.c1655
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.cpp1520
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.desktop5
-rw-r--r--src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.ui227
-rw-r--r--src/VBox/Additions/linux/sharedfolders/.scm-settings29
-rw-r--r--src/VBox/Additions/linux/sharedfolders/Makefile.kmk45
-rw-r--r--src/VBox/Additions/linux/sharedfolders/Makefile.module91
-rw-r--r--src/VBox/Additions/linux/sharedfolders/dirops.c893
-rwxr-xr-xsrc/VBox/Additions/linux/sharedfolders/files_vboxsf89
-rw-r--r--src/VBox/Additions/linux/sharedfolders/lnkops.c119
-rw-r--r--src/VBox/Additions/linux/sharedfolders/mount.vboxsf.c538
-rw-r--r--src/VBox/Additions/linux/sharedfolders/regops.c876
-rw-r--r--src/VBox/Additions/linux/sharedfolders/utils.c883
-rw-r--r--src/VBox/Additions/linux/sharedfolders/vbsfmount.c95
-rw-r--r--src/VBox/Additions/linux/sharedfolders/vbsfmount.h90
-rw-r--r--src/VBox/Additions/linux/sharedfolders/vfsmod.c683
-rw-r--r--src/VBox/Additions/linux/sharedfolders/vfsmod.h181
-rw-r--r--src/VBox/Additions/linux/testcase/TimesyncBackdoor.c93
56 files changed, 18576 insertions, 0 deletions
diff --git a/src/VBox/Additions/linux/Makefile b/src/VBox/Additions/linux/Makefile
new file mode 100644
index 00000000..113ac48e
--- /dev/null
+++ b/src/VBox/Additions/linux/Makefile
@@ -0,0 +1,103 @@
+#
+# Makefile for the VirtualBox Linux Guest Drivers.
+#
+
+#
+# Copyright (C) 2009-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+ifneq ($(KBUILD_EXTMOD),)
+
+# Building from kBuild (make -C <kernel_directory> M=`pwd`).
+# KBUILD_EXTMOD is set to $(M) in this case.
+
+obj-m = vboxguest/ vboxsf/ vboxvideo/
+
+else # ! KBUILD_EXTMOD
+
+KBUILD_VERBOSE =
+
+vboxguest:
+ @echo "=== Building 'vboxguest' module ==="
+ @$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxguest
+ @if [ -f vboxguest/vboxguest.ko ]; then \
+ cp vboxguest/vboxguest.ko .; \
+ else \
+ cp vboxguest/vboxguest.o .; \
+ fi
+ @echo
+
+vboxsf: vboxguest
+ @if [ -d vboxsf ]; then \
+ if [ -f vboxguest/Module.symvers ]; then \
+ cp vboxguest/Module.symvers vboxsf; \
+ fi; \
+ echo "=== Building 'vboxsf' module ==="; \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxsf || exit 1; \
+ if [ -f vboxsf/vboxsf.ko ]; then \
+ cp vboxsf/vboxsf.ko .; \
+ else \
+ cp vboxsf/vboxsf.o .; \
+ fi; \
+ echo; \
+ fi
+
+vboxvideo:
+ @if [ -d vboxvideo ]; then \
+ if [ -f vboxguest/Module.symvers ]; then \
+ cp vboxguest/Module.symvers vboxvideo; \
+ fi; \
+ echo "=== Building 'vboxvideo' module ==="; \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxvideo || exit 1; \
+ if [ -f vboxvideo/vboxvideo.ko ]; then \
+ cp vboxvideo/vboxvideo.ko .; \
+ elif [ -f vboxvideo/vboxvideo.o ]; then \
+ cp vboxvideo/vboxvideo.o .; \
+ fi; \
+ echo; \
+ fi
+
+all: vboxguest vboxsf vboxvideo
+
+install:
+ @$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxguest install
+ @if [ -d vboxsf ]; then \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxsf install; \
+ fi
+ @if [ -d vboxvideo ]; then \
+ $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxvideo install; \
+ fi
+
+clean:
+ @$(MAKE) -C vboxguest clean
+ @if [ -d vboxsf ]; then \
+ $(MAKE) -C vboxsf clean; \
+ fi
+ @if [ -d vboxvideo ]; then \
+ $(MAKE) -C vboxvideo clean; \
+ fi
+ rm -f vboxguest.*o vboxsf.*o vboxvideo.*o
+
+check:
+ @$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxguest check
+
+load:
+ @/sbin/rmmod vboxvideo || true
+ @/sbin/rmmod vboxvfs || true
+ @/sbin/rmmod vboxsf || true
+ @/sbin/rmmod vboxguest || true
+ @/sbin/insmod vboxguest.ko
+ @if [ -f vboxsf.ko ]; then /sbin/insmod vboxsf.ko; fi
+ @if [ -f vboxvideo.ko ]; then /sbin/insmod vboxvideo.ko; fi
+
+.PHONY: vboxguest vboxsf vboxvideo all install clean check load
+
+endif # ! KBUILD_EXTMOD
diff --git a/src/VBox/Additions/linux/Makefile.kmk b/src/VBox/Additions/linux/Makefile.kmk
new file mode 100644
index 00000000..080ef828
--- /dev/null
+++ b/src/VBox/Additions/linux/Makefile.kmk
@@ -0,0 +1,450 @@
+# $Id: Makefile.kmk $
+## @file
+# Makefile for the linux guest additions base directory.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Include sub-makefiles.
+#
+include $(PATH_SUB_CURRENT)/sharedfolders/Makefile.kmk
+include $(PATH_SUB_CURRENT)/drm/Makefile.kmk
+ifdef VBOX_WITH_LIGHTDM_GREETER
+ include $(PATH_SUB_CURRENT)/lightdm-greeter/Makefile.kmk
+endif
+
+
+#
+# Globals
+#
+
+# Basic path components
+VBOX_LNX_ADD_PACKAGE_NAME := VBoxGuestAdditions
+VBOX_LNX_ADD_INST_OUT_DIR := $(PATH_TARGET)/Additions/Installer/linux/
+VBOX_LNX_ADD_INST_DBG_DIR := $(VBOX_LNX_ADD_INST_OUT_DIR)debug/
+VBOX_LNX_ADD_INST_STAGE_DIR := $(VBOX_LNX_ADD_INST_OUT_DIR)install/
+
+# Installation paths for binaries and files
+VBOX_LNX_ADD_INST_BIN_DIR := $(VBOX_LNX_ADD_INST_OUT_DIR)bin/
+VBOX_LNX_ADD_INST_SBIN_DIR := $(VBOX_LNX_ADD_INST_OUT_DIR)sbin/
+VBOX_LNX_ADD_INST_LIB_DIR := $(VBOX_LNX_ADD_INST_OUT_DIR)lib/
+VBOX_LNX_ADD_INST_OTHER_DIR := $(VBOX_LNX_ADD_INST_OUT_DIR)other/
+VBOX_LNX_ADD_INST_KMOD_DIR_BASE := $(VBOX_LNX_ADD_INST_OUT_DIR)src/
+# This is a symbolic link, so no trailing slash.
+VBOX_LNX_ADD_INST_KMOD_PATH := $(VBOX_LNX_ADD_INST_KMOD_DIR_BASE)vboxguest-$(VBOX_VERSION_STRING)
+VBOX_LNX_ADD_INST_INIT_DIR := $(VBOX_LNX_ADD_INST_OUT_DIR)init/
+
+VBOX_LNX_ADD_ARCH_INST_DIRS := \
+ $(VBOX_LNX_ADD_INST_OUT_DIR) \
+ $(VBOX_LNX_ADD_INST_BIN_DIR) \
+ $(VBOX_LNX_ADD_INST_SBIN_DIR) \
+ $(if $(VBOX_WITH_CROGL),$(VBOX_LNX_ADD_INST_LIB_DIR),) \
+ $(VBOX_LNX_ADD_INST_INIT_DIR)
+
+# Installation paths for debug symbols
+VBOX_LNX_ADD_DBG_BIN_DIR := $(VBOX_LNX_ADD_INST_DBG_DIR)bin/
+VBOX_LNX_ADD_DBG_SBIN_DIR := $(VBOX_LNX_ADD_INST_DBG_DIR)sbin/
+VBOX_LNX_ADD_DBG_LIB_DIR := $(VBOX_LNX_ADD_INST_DBG_DIR)lib/
+VBOX_LNX_ADD_DBG_OTHER_DIR := $(VBOX_LNX_ADD_INST_DBG_DIR)other/
+
+VBOX_LNX_ADD_DBG_DIRS := \
+ $(VBOX_LNX_ADD_DBG_BIN_DIR) \
+ $(VBOX_LNX_ADD_DBG_SBIN_DIR) \
+ $(if $(VBOX_WITH_CROGL),$(VBOX_LNX_ADD_DBG_LIB_DIR),) \
+ $(VBOX_LNX_ADD_DBG_OTHER_DIR)
+
+# Script source directories
+VBOX_PATH_LNX_ADD_INST := $(PATH_SUB_CURRENT)/installer/
+VBOX_REL_LNX_ADD_INST := $(subst $(PATH_ROOT)/src/VBox, ../..,$(VBOX_PATH_LNX_ADD_INST))
+VBOX_PATH_X11_ADD_INST := $(PATH_ROOT)/src/VBox/Additions/x11/Installer/
+VBOX_REL_X11_ADD_INST := $(subst $(PATH_ROOT)/src/VBox, ../..,$(VBOX_PATH_X11_ADD_INST))
+VBOX_PATH_LNX_INST_SRC := $(PATH_ROOT)/src/VBox/Installer/linux/
+VBOX_REL_LNX_INST_SRC := $(subst $(PATH_ROOT)/src/VBox, ../..,$(VBOX_PATH_LNX_INST_SRC))
+VBOX_PATH_LNX_HOST_DRV := $(PATH_ROOT)/src/VBox/HostDrivers/linux/
+VBOX_REL_LNX_HOST_DRV := $(subst $(PATH_ROOT)/src/VBox, ../..,$(VBOX_PATH_LNX_HOST_DRV))
+
+# Unset this to speed up things during makefile hacking.
+VBOX_LNX_ADD_INST_DEP_ON_MAKEFILE := $(MAKEFILE_CURRENT)
+
+
+#
+# Targets
+#
+VBOX_SELINUX_CMPLD := $(PATH_SUB_CURRENT)/selinux-fedora/vbox_x11.pp
+VBOX_LNX_ADD_ARCHIVE.x86 := $(PATH_OUT_BASE)/linux.x86/$(KBUILD_TYPE)/bin/additions/VBoxGuestAdditions-x86.tar.bz2
+VBOX_LNX_ADD_ARCHIVE.amd64 := $(PATH_OUT_BASE)/linux.amd64/$(KBUILD_TYPE)/bin/additions/VBoxGuestAdditions-amd64.tar.bz2
+ifndef VBOX_WITH_COMBINED_LINUX_GUEST_PACKAGE
+ VBOX_LNX_ADD_ARCHIVES := $(PATH_STAGE_BIN)/additions/VBoxGuestAdditions-$(KBUILD_TARGET_ARCH).tar.bz2
+else
+ VBOX_LNX_ADD_ARCHIVES := \
+ $(VBOX_LNX_ADD_ARCHIVE.x86) \
+ $(VBOX_LNX_ADD_ARCHIVE.amd64)
+endif
+BLDDIRS += \
+ $(VBOX_LNX_ADD_ARCH_INST_DIRS) \
+ $(VBOX_LNX_ADD_DBG_DIRS) \
+ $(VBOX_LNX_ADD_INST_STAGE_DIR)
+
+# Use VBOX_WITHOUT_LINUX_GUEST_PACKAGE to skip building the .run installer.
+# This will only take effect if you also use VBOX_WITHOUT_ADDITIONS_ISO.
+PACKING += \
+ $(if-expr !defined(VBOX_WITHOUT_LINUX_GUEST_PACKAGE), $(PATH_STAGE_BIN)/additions/VBoxLinuxAdditions.run,) \
+ $(VBOX_LNX_ADD_ARCHIVES) \
+ $(PATH_STAGE_BIN)/additions/VBoxGuestAdditions-dbg.tar.bz2
+OTHER_CLEAN += \
+ $(PACKING) \
+ $(VBOX_LNX_ADD_INST_KMOD_PATH) \
+ $(foreach file, $(VBOX_LNX_ADD_ARCHIVES), $(VBOX_LNX_ADD_INST_STAGE_DIR)$(subst -r$(VBOX_SVN_REV),,$(notdir $(file))))
+
+
+#
+# Files to install
+#
+VBOX_LNX_ADD_STRIP_BIN += \
+ VBoxControl \
+ VBoxClient
+
+VBOX_LNX_ADD_STRIP_SBIN += \
+ VBoxService \
+ $(if $(VBOX_WITH_LIGHTDM_GREETER),vbox-greeter)
+
+ifdef VBOX_WITH_CROGL
+VBOX_LNX_ADD_STRIP_LIB += \
+ VBoxOGLarrayspu.so \
+ VBoxOGLcrutil.so \
+ VBoxOGLerrorspu.so \
+ VBoxOGLfeedbackspu.so \
+ VBoxOGLpackspu.so \
+ VBoxOGLpassthroughspu.so \
+ VBoxOGL.so \
+ VBoxEGL.so
+endif
+
+VBOX_LNX_ADD_STRIP_MOD = \
+ vboxmouse_drv_70.so \
+ vboxmouse_drv_71.so \
+ vboxmouse_drv_13.so \
+ vboxmouse_drv_14.so \
+ vboxmouse_drv_15.so \
+ vboxmouse_drv_16.so \
+ $(addsuffix .so,$(filter-out %_32,$(filter vboxvideo_drv_%,$(DLLS)))) \
+ $(if $(VBOX_WITH_PAM),pam_vbox.so,) \
+ mount.vboxsf
+
+VBOX_LNX_ADD_MOD = \
+ 98vboxadd-xclient \
+ x11config.sh
+
+VBOX_LNX_ADD_STRIP_OBJ = \
+ vboxmouse_drv.o \
+ vboxvideo_drv.o
+
+VBOX_LNX_ADD_INIT = \
+ vboxadd \
+ vboxadd-service \
+ vboxadd-x11
+
+#
+# All the files that go into the archive
+#
+VBOX_LNX_ADD_INST_FILES := \
+ $(addprefix $(VBOX_LNX_ADD_INST_BIN_DIR),$(VBOX_LNX_ADD_STRIP_BIN)) \
+ $(addprefix $(VBOX_LNX_ADD_INST_BIN_DIR),$(VBOX_LNX_ADD_BIN)) \
+ $(addprefix $(VBOX_LNX_ADD_INST_SBIN_DIR),$(VBOX_LNX_ADD_STRIP_SBIN)) \
+ $(addprefix $(VBOX_LNX_ADD_INST_LIB_DIR),$(VBOX_LNX_ADD_STRIP_LIB)) \
+ $(addprefix $(VBOX_LNX_ADD_INST_OTHER_DIR),$(VBOX_LNX_ADD_STRIP_MOD)) \
+ $(addprefix $(VBOX_LNX_ADD_INST_OTHER_DIR),$(VBOX_LNX_ADD_MOD)) \
+ $(addprefix $(VBOX_LNX_ADD_INST_OTHER_DIR),$(VBOX_LNX_ADD_STRIP_OBJ)) \
+ $(addprefix $(VBOX_LNX_ADD_INST_INIT_DIR),$(VBOX_LNX_ADD_INIT))
+
+VBOX_LNX_ADD_DBG_FILES := \
+ $(addprefix $(VBOX_LNX_ADD_DBG_BIN_DIR),$(VBOX_LNX_ADD_STRIP_BIN)) \
+ $(addprefix $(VBOX_LNX_ADD_DBG_SBIN_DIR),$(VBOX_LNX_ADD_STRIP_SBIN)) \
+ $(addprefix $(VBOX_LNX_ADD_DBG_LIB_DIR),$(VBOX_LNX_ADD_STRIP_LIB)) \
+ $(addprefix $(VBOX_LNX_ADD_DBG_OTHER_DIR),$(VBOX_LNX_ADD_STRIP_MOD))
+
+ifdef VBOX_WITH_LIGHTDM_GREETER_PACKING
+VBOX_LNX_ADD_INST_FILES += \
+ $(addprefix $(VBOX_LNX_ADD_INST_SBIN_DIR),vbox-greeter)
+endif
+
+# Cleanup of the installer directory files
+OTHER_CLEAN += $(VBOX_LNX_ADD_INST_FILES) $(VBOX_LNX_ADD_DBG_FILES)
+
+# pattern rules for copying the debug info from the VBOX_LNX_ADD_STRIP_* files to the installation directory
+$(addprefix $(VBOX_LNX_ADD_DBG_BIN_DIR),$(VBOX_LNX_ADD_STRIP_BIN)): \
+ $(VBOX_LNX_ADD_DBG_BIN_DIR)% : $(PATH_STAGE_BIN)/additions/% | $$(dir $$@)
+ $(call MSG_TOOL,copydbg,$<,$@)
+ $(QUIET)objcopy --only-keep-debug $< $@
+
+$(addprefix $(VBOX_LNX_ADD_DBG_SBIN_DIR),$(VBOX_LNX_ADD_STRIP_SBIN)): \
+ $(VBOX_LNX_ADD_DBG_SBIN_DIR)% : $(PATH_STAGE_BIN)/additions/% | $$(dir $$@)
+ $(call MSG_TOOL,copydbg,$<,$@)
+ $(QUIET)objcopy --only-keep-debug $< $@
+
+$(addprefix $(VBOX_LNX_ADD_DBG_LIB_DIR),$(VBOX_LNX_ADD_STRIP_LIB)): \
+ $(VBOX_LNX_ADD_DBG_LIB_DIR)% : $(PATH_STAGE_BIN)/additions/% | $$(dir $$@)
+ $(call MSG_TOOL,copydbg,$<,$@)
+ $(QUIET)objcopy --only-keep-debug $< $@
+
+$(addprefix $(VBOX_LNX_ADD_DBG_OTHER_DIR),$(VBOX_LNX_ADD_STRIP_MOD)): \
+ $(VBOX_LNX_ADD_DBG_OTHER_DIR)% : $(PATH_STAGE_BIN)/additions/% | $$(dir $$@)
+ $(call MSG_TOOL,copydbg,$<,$@)
+ $(QUIET)objcopy --only-keep-debug $< $@
+
+# pattern rule for stripping and copying the VBOX_LNX_ADD_STRIP_BIN files to the installation directory
+$(addprefix $(VBOX_LNX_ADD_INST_BIN_DIR),$(VBOX_LNX_ADD_STRIP_BIN)): \
+ $(VBOX_LNX_ADD_INST_BIN_DIR)% : $(PATH_STAGE_BIN)/additions/% \
+ $(VBOX_LNX_ADD_DBG_BIN_DIR)% \
+ | $$(dir $$@)
+ $(call MSG_INST_FILE,$<,$@)
+ $(QUIET)$(INSTALL) -m 0755 $(if $(VBOX_DO_STRIP),-s,) $< $@
+ $(QUIET)objcopy --add-gnu-debuglink=$(subst $(VBOX_LNX_ADD_INST_BIN_DIR),$(VBOX_LNX_ADD_DBG_BIN_DIR),$@) $@
+
+# pattern rule for stripping and copying the VBOX_LNX_ADD_STRIP_SBIN files to the installation directory
+$(addprefix $(VBOX_LNX_ADD_INST_SBIN_DIR),\
+ $(filter-out vbox-greeter,$(VBOX_LNX_ADD_STRIP_SBIN))): \
+ $(VBOX_LNX_ADD_INST_SBIN_DIR)% : $(PATH_STAGE_BIN)/additions/% \
+ $(VBOX_LNX_ADD_DBG_SBIN_DIR)% \
+ | $$(dir $$@)
+ $(call MSG_INST_FILE,$<,$@)
+ $(QUIET)$(INSTALL) -m 0755 $(if $(VBOX_DO_STRIP),-s,) $< $@
+ $(QUIET)objcopy --add-gnu-debuglink=$(subst $(VBOX_LNX_ADD_INST_SBIN_DIR),$(VBOX_LNX_ADD_DBG_SBIN_DIR),$@) $@
+
+# pattern rule for stripping and copying vbox-greeter to the installation directory
+$(addprefix $(VBOX_LNX_ADD_INST_SBIN_DIR),vbox-greeter): \
+ $(VBOX_LNX_ADD_INST_SBIN_DIR)% : $(subst linux.amd64/release,linux.amd64/release/greeter,$(subst linux.x86/release,linux.x86/release/greeter,$(PATH_STAGE_BIN)))/additions/% \
+ | $$(dir $$@)
+ $(call MSG_INST_FILE,$<,$@)
+ $(QUIET)$(INSTALL) -m 0755 $< $@
+
+# pattern rule for stripping and copying the VBOX_LNX_ADD_STRIP_LIB files to the installation directory
+$(addprefix $(VBOX_LNX_ADD_INST_LIB_DIR),$(VBOX_LNX_ADD_STRIP_LIB)): \
+ $(VBOX_LNX_ADD_INST_LIB_DIR)% : $(PATH_STAGE_BIN)/additions/% \
+ $(VBOX_LNX_ADD_DBG_LIB_DIR)% \
+ | $$(dir $$@)
+ $(call MSG_INST_FILE,$<,$@)
+ $(QUIET)$(INSTALL) -m 0755 $(if $(VBOX_DO_STRIP),-s,) $< $@
+ $(QUIET)objcopy --add-gnu-debuglink=$(subst $(VBOX_LNX_ADD_INST_LIB_DIR),$(VBOX_LNX_ADD_DBG_LIB_DIR),$@) $@
+
+# pattern rule for stripping and copying the VBOX_LNX_ADD_STRIP_MOD files to the installation directory
+$(addprefix $(VBOX_LNX_ADD_INST_OTHER_DIR),$(VBOX_LNX_ADD_STRIP_MOD)): \
+ $(VBOX_LNX_ADD_INST_OTHER_DIR)% : $(PATH_STAGE_BIN)/additions/% \
+ $(VBOX_LNX_ADD_DBG_OTHER_DIR)% \
+ | $$(dir $$@)
+ $(call MSG_INST_FILE,$<,$@)
+ $(QUIET)$(INSTALL) -m 0755 $(if $(VBOX_DO_STRIP),-s,) $< $@
+ $(QUIET)objcopy --add-gnu-debuglink=$(subst $(VBOX_LNX_ADD_INST_OTHER_DIR),$(VBOX_LNX_ADD_DBG_OTHER_DIR),$@) $@
+
+# pattern rule for stripping and copying the VBOX_LNX_ADD_STRIP_OBJ files to the installation directory
+$(addprefix $(VBOX_LNX_ADD_INST_OTHER_DIR),$(VBOX_LNX_ADD_STRIP_OBJ)): \
+ $(VBOX_LNX_ADD_INST_OTHER_DIR)% : $(PATH_STAGE_BIN)/additions/% | $$(dir $$@)
+ $(call MSG_INST_FILE,$<,$@)
+ifeq ($(VBOX_DO_STRIP),)
+ $(QUIET)$(INSTALL) -m 0644 $< $@
+else # strip to temp file because of umask.
+ $(QUIET)objcopy --strip-unneeded -R .comment $< $@.tmp
+ $(QUIET)$(INSTALL) -m 0644 $@.tmp $@
+ $(QUIET)$(RM) -f -- $@.tmp
+endif
+
+include $(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest/linux/files_vboxguest
+VBOX_LNX_ADD_INST_FILES_VBOXGUEST=$(patsubst =>%,$(PATH_STAGE_BIN)/additions/src/vboxguest/%,$(filter =>%,$(subst =>, =>,$(subst $(DQUOTE),,$(FILES_VBOXGUEST_NOBIN)))))
+include $(PATH_ROOT)/src/VBox/Additions/linux/sharedfolders/files_vboxsf
+VBOX_LNX_ADD_INST_FILES_VBOXSF=$(patsubst =>%,$(PATH_STAGE_BIN)/additions/src/vboxsf/%,$(filter =>%,$(subst =>, =>,$(subst $(DQUOTE),,$(FILES_VBOXSF_NOBIN)))))
+include $(PATH_ROOT)/src/VBox/Additions/linux/drm/files_vboxvideo_drv
+VBOX_LNX_ADD_INST_FILES_VBOXVIDEO=$(patsubst =>%,$(PATH_STAGE_BIN)/additions/src/vboxvideo/%,$(filter =>%,$(subst =>, =>,$(subst $(DQUOTE),,$(FILES_VBOXVIDEO_DRM_NOBIN)))))
+
+# special rule for the kernel modules
+$(VBOX_LNX_ADD_INST_KMOD_DIR_BASE): \
+ $(PATH_STAGE_BIN)/additions/src/ \
+ $(PATH_ROOT)/Version.kmk \
+ | $(dir $@)
+ $(call MSG_INST_SYM,$<,$@)
+ $(QUIET)$(RM) -Rf $@
+ $(QUIET)$(MKDIR) $@
+ $(QUIET)$(LN_SYMLINK) $< $(VBOX_LNX_ADD_INST_KMOD_PATH)
+
+INSTALLS += GuestDrivers-src
+GuestDrivers-src_INST = $(INST_ADDITIONS)src/
+GuestDrivers-src_MODE = a+r,u+w
+GuestDrivers-src_SOURCES = Makefile
+
+
+INSTALLS += lnx_add_inst-exec
+lnx_add_inst-exec_INST = $(VBOX_LNX_ADD_INST_OTHER_DIR)
+lnx_add_inst-exec_INSTTYPE = stage
+lnx_add_inst-exec_EXEC_SOURCES = \
+ $(VBOX_REL_X11_ADD_INST)98vboxadd-xclient \
+ $(VBOX_REL_X11_ADD_INST)x11config.sh \
+ $(VBOX_REL_LNX_INST_SRC)check_module_dependencies.sh
+
+
+INSTALLS += lnx_add_inst-noexec
+lnx_add_inst-noexec_INST = $(VBOX_LNX_ADD_INST_OTHER_DIR)
+lnx_add_inst-noexec_INSTTYPE = stage
+lnx_add_inst-noexec_SOURCES = \
+ $(VBOX_REL_X11_ADD_INST)vboxclient.desktop \
+ $(VBOX_REL_X11_ADD_INST)vboxvideo.ids \
+ $(if $(VBOX_WITH_LIGHTDM_GREETER_PACKING),lightdm-greeter/vbox-greeter.desktop,) \
+ selinux-fedora/vbox_x11.pp \
+ selinux-fedora/vbox_accel.pp
+
+INSTALLS += lnx_add_inst-license
+lnx_add_inst-license_INST = $(VBOX_LNX_ADD_INST_OUT_DIR)
+lnx_add_inst-license_INSTTYPE = stage
+lnx_add_inst-license_SOURCES = \
+ $(VBOX_BRAND_LICENSE_TXT)=>LICENSE
+
+
+#
+# We need our routines.sh and the uninstallation scripts in the staging
+# directory too
+#
+INSTALLS += LnxAdd-scripts
+LnxAdd-scripts_INST = $(VBOX_LNX_ADD_INST_STAGE_DIR)
+LnxAdd-scripts_INSTTYPE = stage
+LnxAdd-scripts_SOURCES = \
+ $(VBOX_REL_LNX_ADD_INST)deffiles
+LnxAdd-scripts_EXEC_SOURCES = \
+ $(VBOX_REL_LNX_INST_SRC)routines.sh
+
+ifdef VBOX_WITH_LIGHTDM_GREETER_PACKING
+ LnxAdd-scripts_EXEC_SOURCES += \
+ $(VBOX_REL_LNX_ADD_INST)module-autologon.sh=>installer/module-autologon
+endif
+
+
+#
+# And the init scripts
+#
+INSTALLS += LnxAdd-init-scripts
+LnxAdd-init-scripts_INST = $(VBOX_LNX_ADD_INST_INIT_DIR)
+LnxAdd-init-scripts_INSTTYPE = stage
+LnxAdd-init-scripts_EXEC_SOURCES = \
+ $(foreach i,$(VBOX_LNX_ADD_INIT), installer/$(i).sh=>$(i))
+
+# this file needs editing before it can be included in the generic installer.
+$(VBOX_LNX_ADD_INST_STAGE_DIR)install.sh: \
+ $(VBOX_PATH_LNX_ADD_INST)install.sh.in \
+ $(VBOX_VERSION_STAMP) | $$(dir $$@)
+ $(RM) -f -- $@
+ $(QUIET)$(SED) \
+ -e "s;_VERSION_;$(VBOX_VERSION_STRING);g" \
+ -e "s;_BUILDTYPE_;$(KBUILD_TYPE);g" \
+ -e "s;_USERNAME_;$(USERNAME);g" \
+ --output $@ \
+ $<
+ $(QUIET)$(CHMOD) 0755 $@
+OTHER_CLEAN += \
+ $(VBOX_LNX_ADD_INST_OUT_DIR)install.sh \
+ $(VBOX_LNX_ADD_INST_STAGE_DIR)install.sh
+
+
+#
+# Build test for the Guest Additions kernel modules (kmk check).
+#
+$(evalcall2 VBOX_LINUX_KMOD_TEST_BUILD_RULE_FN,vboxsf-src,vboxguest-src,)
+$(evalcall2 VBOX_LINUX_KMOD_TEST_BUILD_RULE_FN,vboxvideo-src,,)
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
+
+# All the files that go into our archive
+VBOX_LNX_ADD_ARCH_FILES = \
+ $(lnx_add_inst-noexec_2_STAGE_TARGETS) \
+ $(lnx_add_inst-exec_2_STAGE_TARGETS) \
+ $(lnx_add_inst-license_2_STAGE_TARGETS) \
+ $(VBOX_LNX_ADD_INST_FILES) \
+ $(VBOX_LNX_ADD_INST_KMOD_DIR_BASE)
+
+VBOX_LNX_ADD_INST_ARCH_DEPS := \
+ $(VBOX_LNX_ADD_ARCH_FILES) \
+ $(VBOX_LNX_ADD_INST_DEP_ON_MAKEFILE) \
+ $(VBOX_VERSION_STAMP) \
+ $(VBOX_LNX_ADD_INST_FILES_VBOXGUEST) \
+ $(VBOX_LNX_ADD_INST_FILES_VBOXSF) \
+ $(VBOX_LNX_ADD_INST_FILES_VBOXVIDEO)
+
+#
+# .tar.bz2 for converting into .run
+#
+$(VBOX_LNX_ADD_ARCHIVE.$(KBUILD_TARGET_ARCH)): \
+ $(VBOX_LNX_ADD_INST_ARCH_DEPS)
+ $(call MSG_L1,Packing $@)
+ $(QUIET)$(RM) -f -- $(wildcard $(dir $@)VBoxGuestAdditions-*r*.tar.bz2)
+ $(QUIET)$(MKDIR) -p $(@D)
+ $(QUIET)$(CHMOD) 0755 $(VBOX_LNX_ADD_ARCH_INST_DIRS)
+ifdef VBOX_USE_PBZIP2
+ $(QUIET)tar --dereference --owner 0 --group 0 -cRf $(patsubst %.bz2,%,$@) \
+ -C $(VBOX_LNX_ADD_INST_OUT_DIR) \
+ LICENSE bin init lib other sbin src \
+ $(if $(filter $(KBUILD_TYPE),debug),debug)
+ $(QUIET)pbzip2 $(patsubst %.bz2,%,$@)
+else
+ $(QUIET)tar --dereference --owner 0 --group 0 --ignore-failed-read -cjRf $@ \
+ -C $(VBOX_LNX_ADD_INST_OUT_DIR) \
+ LICENSE bin init lib other sbin src \
+ $(if $(filter $(KBUILD_TYPE),debug),debug)
+endif
+ $(QUIET)$(CHMOD) 0644 $@
+
+
+#
+# .tar.bz2 containing the debug information
+#
+$(PATH_STAGE_BIN)/additions/VBoxGuestAdditions-dbg.tar.bz2: \
+ $(VBOX_LNX_ADD_DBG_FILES) \
+ $(VBOX_LNX_ADD_INST_DEP_ON_MAKEFILE)
+ $(call MSG_L1,Packing $@)
+ $(QUIET)$(RM) -f -- $@ $(patsubst %.bz2,%,$@)
+ $(QUIET)$(MKDIR) -p $(@D)
+ $(QUIET)$(CHMOD) 0755 $(VBOX_LNX_ADD_DBG_DIRS)
+ifdef VBOX_USE_PBZIP2
+ $(QUIET)tar --dereference --owner 0 --group 0 -cRf $(patsubst %.bz2,%,$@) \
+ -C $(VBOX_LNX_ADD_INST_DBG_DIR) \
+ bin lib sbin
+ $(QUIET)pbzip2 $(patsubst %.bz2,%,$@)
+else
+ $(QUIET)tar --dereference --owner 0 --group 0 --ignore-failed-read -cjRf $@ \
+ -C $(VBOX_LNX_ADD_INST_DBG_DIR) \
+ bin lib sbin
+endif
+ $(QUIET)$(CHMOD) 0644 $@
+
+
+#
+# Build the Linux Guest Additions self extracting installer.
+#
+# Note that $(PATH_SUB_CURRENT) was changed by subfooter.kmk above and
+# any references should be made via variables assigned a know value via := .
+#
+$(PATH_STAGE_BIN)/additions/VBoxLinuxAdditions.run: \
+ $(VBOX_LNX_ADD_ARCHIVES) \
+ $(VBOX_LNX_ADD_INST_STAGE_DIR)install.sh \
+ $$(LnxAdd-scripts_2_STAGE_TARGETS) \
+ $(VBOX_VERSION_STAMP)
+ # Remove any archives left over from previous builds so that they don't
+ # end up in our installer as well.
+ $(QUIET)$(RM) -f $(foreach file, $(wildcard $(VBOX_LNX_ADD_INST_STAGE_DIR)$(VBOX_LNX_ADD_PACKAGE_NAME)-*.tar.bz2), $(file))
+ $(QUIET)$(foreach file, $(VBOX_LNX_ADD_ARCHIVES), \
+ $(CP) -f $(file) $(VBOX_LNX_ADD_INST_STAGE_DIR)$(subst -r$(VBOX_SVN_REV),,$(notdir $(file)))$(NLTAB) )
+ $(QUIET)$(VBOX_MAKESELF) --nocomp $(VBOX_LNX_ADD_INST_STAGE_DIR) $@ \
+ "VirtualBox $(VBOX_VERSION_STRING) Guest Additions for Linux" \
+ /bin/sh ./install.sh "\$$0 1> /dev/null"
+
diff --git a/src/VBox/Additions/linux/drm/.scm-settings b/src/VBox/Additions/linux/drm/.scm-settings
new file mode 100644
index 00000000..cd4a860a
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/.scm-settings
@@ -0,0 +1,25 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for linux drm driver.
+#
+
+#
+# Copyright (C) 2010-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+
+#Sources are MIT licensed for simplier upstreaming, several files are external.
+/*.c|/*.h: --license-based-on-mit --no-convert-tabs
+/vbox_hgsmi.c: --license-mit
+/Makefile.module.kms: --treat-as Makefile
+
+--filter-out-files /README.testing
+
diff --git a/src/VBox/Additions/linux/drm/Makefile.kmk b/src/VBox/Additions/linux/drm/Makefile.kmk
new file mode 100644
index 00000000..29338a19
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/Makefile.kmk
@@ -0,0 +1,42 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the vboxvideo DRM module (linux kernel OpenGL module).
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Populate FILES_VBOXVIDEO_DRM_NOBIN
+#
+INSTALLS += vboxvideo-src
+include $(PATH_SUB_CURRENT)/files_vboxvideo_drv
+vboxvideo-src_DEPS = \
+ $(PATH_ROOT)/src/VBox/Additions/linux/drm/files_vboxvideo_drv \
+ $(PATH_ROOT)/src/VBox/Additions/linux/drm/indent.sed
+vboxvideo-src_INST = $(INST_ADDITIONS)src/vboxvideo/
+vboxvideo-src_SOURCES = \
+ $(subst $(DQUOTE),,$(FILES_VBOXVIDEO_DRM_NOBIN))
+vboxvideo-src_EXEC_SOURCES = \
+ $(subst $(DQUOTE),,$(FILES_VBOXVIDEO_DRM_BIN))
+vboxvideo-src_INSTALLER = $(RM_EXT) -f -- "$2" && \
+ $(if $(filter %.c %.h,$2),$(SED) -f $(PATH_ROOT)/src/VBox/Additions/linux/drm/indent.sed \
+ --output "$2" "$1",$(CP_EXT) "$1" "$2") && \
+ $(CHMOD_EXT) "$(if $(mode),$(mode),0644)" "$2"
+
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/linux/drm/Makefile.module.kms b/src/VBox/Additions/linux/drm/Makefile.module.kms
new file mode 100644
index 00000000..cad8263d
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/Makefile.module.kms
@@ -0,0 +1,51 @@
+# $Id: Makefile.module.kms $
+## @file
+# VirtualBox Guest Additions Module Makefile.
+#
+# (For 2.6.x this file must be 'Makefile'!)
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+# Linux kbuild sets this to our source directory if we are called from there
+obj ?= $(CURDIR)
+include $(obj)/Makefile.include.header
+
+BUILD =
+
+# We want to build on Linux 3.11 and later and on all EL 7 kernels.
+ifneq ($(filter-out 1.% 2.% 3.0.% 3.1.% 3.2.% 3.3.% 3.4.% 3.5.% 3.6.% 3.7.% \
+ 3.8.% 3.9.% 3.10.%,$(KERN_VER)),)
+ BUILD = 1
+endif
+ifeq ($(filter-out %.el7.x86_64,$(KERN_VER)),)
+ BUILD = 1
+endif
+
+ifneq ($(BUILD),)
+
+MOD_NAME = vboxvideo
+MOD_OBJS = hgsmi_base.o \
+ modesetting.o vbox_drv.o vbox_fb.o vbox_irq.o vbox_main.o \
+ vbox_mode.o vbox_ttm.o vbva_base.o vbox_prime.o vbox_hgsmi.o
+MOD_INCL = -I$(KBUILD_EXTMOD) -Iinclude/drm
+
+include $(obj)/Makefile.include.footer
+
+else # ! wildcard $(KERN_INCL)/drm/drm_rect.h
+
+ all:
+ install:
+ clean:
+
+endif # ! wildcard $(KERN_INCL)/drm/drm_rect.h
diff --git a/src/VBox/Additions/linux/drm/README.testing b/src/VBox/Additions/linux/drm/README.testing
new file mode 100644
index 00000000..72cd96c3
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/README.testing
@@ -0,0 +1,13 @@
+This document lists things which have been known to fail in the past with the
+drm video driver and which should be tested regularly, e.g. when making code
+changes or before releases.
+
+ * Test that "auto-resize" is enabled in the GUI if user space supports it.
+ * Test that old versions of Plymouth which do not report rectangles
+ (pre-0.9.0/2014-05-20) update the screen correctly.
+ * Having valid, non-overlapping offset hints on all screens has caused
+ mouse integration and/or screen updates to break for fbdev, X.Org or
+ GNOME Shell/Wayland.
+ * Note that if a multi-screen VM is booted with only one screen enabled
+ the fbdev console will be disabled on the others until reboot. Test this
+ configuration.
diff --git a/src/VBox/Additions/linux/drm/files_vboxvideo_drv b/src/VBox/Additions/linux/drm/files_vboxvideo_drv
new file mode 100755
index 00000000..c981a5d5
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/files_vboxvideo_drv
@@ -0,0 +1,49 @@
+#!/bin/sh
+# $Id: files_vboxvideo_drv $
+## @file
+# Shared file between Makefile.kmk and export_modules.sh.
+#
+
+#
+# Copyright (C) 2011-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+FILES_VBOXVIDEO_DRM_NOBIN=" \
+ ${PATH_OUT}/version-generated.h=>version-generated.h \
+ ${PATH_OUT}/revision-generated.h=>revision-generated.h \
+ ${PATH_OUT}/product-generated.h=>product-generated.h \
+ ${PATH_ROOT}/include/VBox/Graphics/VBoxVideo.h=>vboxvideo.h \
+ ${PATH_ROOT}/include/VBox/Graphics/VBoxVideoGuest.h=>vboxvideo_guest.h \
+ ${PATH_ROOT}/include/VBox/Graphics/HGSMIChannels.h=>hgsmi_channels.h \
+ ${PATH_ROOT}/include/VBox/Graphics/HGSMIChSetup.h=>hgsmi_ch_setup.h \
+ ${PATH_ROOT}/include/VBox/Graphics/HGSMIContext.h=>hgsmi_context.h \
+ ${PATH_ROOT}/include/VBox/Graphics/HGSMIDefs.h=>hgsmi_defs.h \
+ ${PATH_ROOT}/include/VBox/Graphics/VBoxVideoErr.h=>vbox_err.h \
+ ${PATH_ROOT}/include/VBox/Graphics/VBoxVideoVBE.h=>vboxvideo_vbe.h \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp=>hgsmi_base.c \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxVideo/Modesetting.cpp=>modesetting.c \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp=>vbva_base.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_drv.c=>vbox_drv.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_drv.h=>vbox_drv.h \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_fb.c=>vbox_fb.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_irq.c=>vbox_irq.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_main.c=>vbox_main.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_mode.c=>vbox_mode.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_prime.c=>vbox_prime.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_ttm.c=>vbox_ttm.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_hgsmi.c=>vbox_hgsmi.c \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile.include.header=>Makefile.include.header \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile.include.footer=>Makefile.include.footer \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/Makefile.module.kms=>Makefile \
+"
+
+FILES_VBOXVIDEO_DRM_BIN=" \
+"
diff --git a/src/VBox/Additions/linux/drm/indent.sed b/src/VBox/Additions/linux/drm/indent.sed
new file mode 100644
index 00000000..cb8b5b74
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/indent.sed
@@ -0,0 +1,274 @@
+# Oracle VM VirtualBox
+# VirtualBox to Linux kernel coding style conversion script.
+
+#
+# Copyright (C) 2017-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+# This script is for converting code inside the vboxvideo module to Linux
+# kernel coding style. It assumes correct VirtualBox coding style, will break
+# break if the coding style is wrong (e.g. tab instead of spaces) and is not
+# indended to be a generic solution: for example, identifiers will be
+# translated case by case, not algorithmically. It also assumes that any
+# flexibility in either coding style will be used where possible to make the
+# code conform to both at once.
+
+# Replace up to six leading groups of four spaces with tabs.
+s/^ */\t\t\t\t\t\t/g
+s/^ /\t\t\t\t\t/g
+s/^ /\t\t\t\t/g
+s/^ /\t\t\t/g
+s/^ /\t\t/g
+s/^ /\t/g
+
+# Change various symbols and file names to fit kernel conventions.
+
+# Miscellaneous:
+# Remove @file headers.
+\|/\*\* @file| {
+:start
+ N
+ s|\*/|\*/|g
+ T start
+ N
+ d
+}
+/^\/\* \$Id:.*\*\/$/d
+/^typedef .* HGSMIOFFSET;/d
+/^typedef .* HGSMISIZE;/d
+s/^#\( *\)include <\([^/]*\)>/#\1include "\2"/g
+
+# File names:
+s/\bHGSMIBase\.h\b/vbox_drv.h/g
+s/\bHGSMIChannels\.h\b/hgsmi_channels.h/g
+s/\bHGSMIChSetup\.h\b/hgsmi_ch_setup.h/g
+s/\bHGSMIContext\.h\b/hgsmi_context.h/g
+s/\bHGSMIDefs\.h\b/hgsmi_defs.h/g
+s/\bVBoxVideoGuest\.h\b/vboxvideo_guest.h/g
+s/\bVBoxVideo\.h\b/vboxvideo.h/g
+s/\bVBoxVideoIPRT\.h\b/vbox_err.h/g
+s/\bVBoxVideoVBE\.h\b/vboxvideo_vbe.h/g
+
+# Function names:
+s/\btestQueryConf\b/hgsmi_test_query_conf/g
+s/\bVBoxHGSMIBufferAlloc\b/hgsmi_buffer_alloc/g
+s/\bVBoxHGSMIBufferFree\b/hgsmi_buffer_free/g
+s/\bVBoxHGSMIBufferSubmit\b/hgsmi_buffer_submit/g
+s/\bVBoxHGSMICursorPosition\b/hgsmi_cursor_position/g
+s/\bVBoxHGSMIGetModeHints\b/hgsmi_get_mode_hints/g
+s/\bVBoxHGSMIProcessDisplayInfo\b/hgsmi_process_display_info/g
+s/\bVBoxHGSMIReportFlagsLocation\b/hgsmi_report_flags_location/g
+s/\bVBoxHGSMISendCapsInfo\b/hgsmi_send_caps_info/g
+s/\bVBoxHGSMIUpdateInputMapping\b/hgsmi_update_input_mapping/g
+s/\bVBoxHGSMIUpdatePointerShape\b/hgsmi_update_pointer_shape/g
+s/\bvboxHwBufferAvail\b/vbva_buffer_available/g
+s/\bvboxHwBufferEndUpdate\b/vbva_buffer_end_update/g
+s/\bvboxHwBufferFlush\b/vbva_buffer_flush/g
+s/\bvboxHwBufferPlaceDataAt\b/vbva_buffer_place_data_at/g
+s/\bvboxHwBufferWrite\b/vbva_write/g
+s/\bVBoxQueryConfHGSMI\b/hgsmi_query_conf/g
+s/\bVBoxVBVABufferBeginUpdate\b/vbva_buffer_begin_update/g
+s/\bVBoxVBVABufferEndUpdate\b/vbva_buffer_end_update/g
+s/\bVBoxVBVADisable\b/vbva_disable/g
+s/\bVBoxVBVAEnable\b/vbva_enable/g
+s/\bvboxVBVAInformHost\b/vbva_inform_host/g
+s/\bvboxVBVASetupBufferContext\b/vbva_setup_buffer_context/g
+s/\bVBVO_PORT_READ_U8\b/inb/g
+s/\bVBVO_PORT_READ_U16\b/inw/g
+s/\bVBVO_PORT_READ_U32\b/inl/g
+s/\bVBVO_PORT_WRITE_U8\b *( *\(\b[^(),]*\b\) *, *\(\b[^(),]*\b\) *)/outb(\2, \1)/g
+s/\bVBVO_PORT_WRITE_U16\b *( *\(\b[^(),]*\b\) *, *\(\b[^(),]*\b\) *)/outw(\2, \1)/g
+s/\bVBVO_PORT_WRITE_U32\b *( *\(\b[^(),]*\b\) *, *\(\b[^(),]*\b\) *)/outl(\2, \1)/g
+s/\bVBVO_PORT_WRITE_U[0-9]*\b/VBVO_PORT_WRITE_statement_should_be_on_one_line/g
+
+# Macros:
+s/\b_1K\b/1024/g
+s/\b_4M\b/4*1024*1024/g
+s/\bAssert\b\([^;]*\);/WARN_ON_ONCE(!(\1));/g
+s/\bAssertCompile\b/assert_compile/g
+s/\bAssertCompileSize\b/assert_compile_size/g
+s/\bAssertPtr\b\([^;]*\);/WARN_ON_ONCE(!(\1));/g
+s/\bAssertPtrReturn\b/assert_ptr_return/g
+/AssertPtrNullReturnVoid/d
+s/\bAssertRC\b\([^;]*\);/WARN_ON_ONCE(RT_FAILURE\1);/g
+s/\bAssertRC\b/Assert_RC_statement_should_be_on_one_line/g
+s/\bDECLCALLBACK\b(\([^)]*\))/\1/g
+s/\bDECLCALLBACKMEMBER\b(\([^,)]*\), *\([^,)]*\))/\1 (*\2)/g
+s/^\bDECLHIDDEN\b(\([^)]*\))/\1/g
+s/\bDECLINLINE\b(\([^)]*\))/static inline \1/g
+s/\bRT_BIT\b/BIT/g
+s/\bRT_BOOL\b(\([^)]*\))/(!!(\1))/g
+/RT_C_DECLS/d
+s/\bUINT16_MAX\b/U16_MAX/g
+s/\bUINT32_MAX\b/U32_MAX/g
+s/\bUINT32_C\b(\(.*\))/\1u/g
+s/!VALID_PTR(/WARN_ON(!/g
+s/\bRT_UNTRUSTED_VOLATILE_HOST\b//g
+s/\bRT_UNTRUSTED_VOLATILE_GUEST\b//g
+s/\bRT_UNTRUSTED_VOLATILE_HSTGST\b//g
+
+# Type names:
+s/\bint32_t\b/s32/g
+s/\buint8_t\b/u8/g
+s/\buint16_t\b/u16/g
+s/\buint32_t\b/u32/g
+s/(HGSMIBUFFERLOCATION \*)//g # Remove C++ casts from void.
+s/typedef struct HGSMIBUFFERLOCATION/struct hgsmi_buffer_location/g
+s/struct HGSMIBUFFERLOCATION/struct hgsmi_buffer_location/g
+s/} HGSMIBUFFERLOCATION/}/g
+s/\bHGSMIBUFFERLOCATION\b/struct hgsmi_buffer_location/g
+s/\([^*] *\)\bPHGSMIGUESTCOMMANDCONTEXT\b/\1struct gen_pool */g
+s/(HGSMIHOSTFLAGS \*)//g # Remove C++ casts from void.
+s/typedef struct HGSMIHOSTFLAGS/struct hgsmi_host_flags/g
+s/struct HGSMIHOSTFLAGS/struct hgsmi_host_flags/g
+s/} HGSMIHOSTFLAGS/}/g
+s/\bHGSMIHOSTFLAGS\b/struct hgsmi_host_flags/g
+s/\bHGSMIOFFSET\b/u32/g
+s/\bHGSMISIZE\b/u32/g
+s/\bRTRECT\b/void/g
+s/(VBVABUFFERCONTEXT \*)//g # Remove C++ casts from void.
+s/struct VBVABUFFERCONTEXT/struct vbva_buf_context/g
+s/} VBVABUFFERCONTEXT/} vbva_buf_context/g
+s/\bVBVABUFFERCONTEXT\b/struct vbva_buf_context/g
+s/\([^*] *\)\bPVBVABUFFERCONTEXT\b/\1struct vbva_buf_context */g
+s/(VBVACAPS \*)//g # Remove C++ casts from void.
+s/struct VBVACAPS/struct vbva_caps/g
+s/} VBVACAPS/} vbva_caps/g
+s/\bVBVACAPS\b/struct vbva_caps/g
+s/(VBVACONF32 \*)//g # Remove C++ casts from void.
+s/struct VBVACONF32/struct vbva_conf32/g
+s/} VBVACONF32/} vbva_conf32/g
+s/\bVBVACONF32\b/struct vbva_conf32/g
+s/(VBVACURSORPOSITION \*)//g # Remove C++ casts from void.
+s/struct VBVACURSORPOSITION/struct vbva_cursor_position/g
+s/} VBVACURSORPOSITION/} vbva_cursor_position/g
+s/\bVBVACURSORPOSITION\b/struct vbva_cursor_position/g
+s/(VBVAENABLE_EX \*)//g # Remove C++ casts from void.
+s/struct VBVAENABLE_EX/struct vbva_enable_ex/g
+s/} VBVAENABLE_EX/} vbva_enable_ex/g
+s/\bVBVAENABLE_EX\b/struct vbva_enable_ex/g
+s/(VBVAMOUSEPOINTERSHAPE \*)//g # Remove C++ casts from void.
+s/struct VBVAMOUSEPOINTERSHAPE/struct vbva_mouse_pointer_shape/g
+s/} VBVAMOUSEPOINTERSHAPE/} vbva_mouse_pointer_shape/g
+s/\bVBVAMOUSEPOINTERSHAPE\b/struct vbva_mouse_pointer_shape/g
+s/(VBVAMODEHINT \*)//g # Remove C++ casts from void.
+s/struct VBVAMODEHINT/struct vbva_modehint/g
+s/} VBVAMODEHINT/} vbva_modehint/g
+s/\bVBVAMODEHINT\b/struct vbva_modehint/g
+s/(VBVAQUERYMODEHINTS \*)//g # Remove C++ casts from void.
+s/struct VBVAQUERYMODEHINTS/struct vbva_query_mode_hints/g
+s/} VBVAQUERYMODEHINTS/} vbva_query_mode_hints/g
+s/\bVBVAQUERYMODEHINTS\b/struct vbva_query_mode_hints/g
+s/(VBVAREPORTINPUTMAPPING \*)//g # Remove C++ casts from void.
+s/struct VBVAREPORTINPUTMAPPING/struct vbva_report_input_mapping/g
+s/} VBVAREPORTINPUTMAPPING/} vbva_report_input_mapping/g
+s/\bVBVAREPORTINPUTMAPPING\b/struct vbva_report_input_mapping/g
+
+# Variable and parameter names:
+s/\baRecords\b/records/g
+s/\bau8Data\b/data/g
+s/\bau32Reserved\b/reserved/g
+s/\bBase\b/base/g
+s/\bbEnable\b/enable/g
+s/\bbRc\b/ret/g
+s/\bcb\b/len/g
+s/\bcbBuffer\b/buffer_length/g
+s/\bcbChunk\b/chunk/g
+s/\bcbData\b/data_len/g
+s/\bcbHintsStructureGuest\b/hints_structure_guest_size/g
+s/\bcbHwBufferAvail\b/available/g
+s/\bcbLength\b/len/g
+s/\bcbLocation\b/buf_len/g
+s/\bcbPartialWriteThreshold\b/partial_write_tresh/g ## @todo fix this?
+s/\bcbPitch\b/pitch/g
+s/\bcbPixels\b/pixel_len/g
+s/\bcBPP\b/bpp/g
+s/\bcbRecord\b/len_and_flags/g ## @todo fix this?
+s/\bcDisplay\b/display/g
+s/\bcHeight\b/height/g
+s/\bcHintsQueried\b/hints_queried_count/g
+s/\bcHotX\b/hot_x/g
+s/\bcHotY\b/hot_y/g
+s/\bcOriginX\b/origin_x/g
+s/\bcOriginY\b/origin_y/g
+s/\bcScreen\b/screen/g
+s/\bcScreens\b/screens/g
+s/\bcWidth\b/width/g
+s/\bfCaps\b/caps/g
+s/\bfFlags\b/flags/g
+s/\bfHwBufferOverflow\b/buffer_overflow/g
+s/\bfReportPosition\b/report_position/g
+s/\bfu32Flags\b/flags/g
+s/\bhostFlags\b/host_flags/g
+s/\bi32Diff\b/diff/g
+s/\bi32OriginX\b/origin_x/g
+s/\bi32OriginY\b/origin_y/g
+s/\bi32Result\b/result/g
+s/\bindexRecordFirst\b/first_record_index/g
+s/\bindexRecordFree\b/free_record_index/g
+s/\bindexRecordNext\b/next/g
+s/\boff32Data\b/data_offset/g
+s/\boff32Free\b/free_offset/g
+s/\boffLocation\b/location/g
+s/\boffStart\b/start_offset/g
+s/\boffVRAMBuffer\b/buffer_offset/g
+s/\bpaHints\b/hints/g
+s/\bpCtx\b/ctx/g
+s/\bpPixels\b/pixels/g
+s/\bpRecord\b/record/g
+s/\bpulValue\b/value_ret/g
+s/\bpVBVA\b/vbva/g
+s/\bpxHost\b/x_host/g
+s/\bpyHost\b/y_host/g
+s/\bu16BitsPerPixel\b/bits_per_pixel/g
+s/\bu16Flags\b/flags/g
+s/\bu32BytesTillBoundary\b/bytes_till_boundary/g
+s/\bu32Flags\b/flags/g
+s/\bu32Height\b/height/g
+s/\bu32HostEvents\b/host_events/g
+s/\bu32HostFlags\b/host_flags/g
+s/\bu32HotX\b/hot_x/g
+s/\bu32HotY\b/hot_y/g
+s/\bu32Index\b/index/g
+s/\bu32LineSize\b/line_size/g
+s/\bu32Offset\b/offset/g
+s/\bu32Reserved\b/reserved/g
+s/\bu32ScreenId\b/screen_id/g
+s/\bu32StartOffset\b/start_offset/g
+s/\bu32SupportedOrders\b/supported_orders/g
+s/\bu32Value\b/value/g
+s/\bu32ViewIndex\b/view_index/g
+s/\bu32Width\b/width/g
+s/\bulValue\b/value/g
+
+# Header file guard:
+s/__HGSMIChannels_h__/__HGSMI_CHANNELS_H__/g
+s/VBOX_INCLUDED_Graphics_HGSMIChSetup_h/__HGSMI_CH_SETUP_H__/g
+
+# And move braces. This must be the last expression as it jumps to the next
+# line.
+/..*$/ {
+ N
+ s/^\([\t ][\t ]*\)} *\n[\t ]*else/\1} else/g
+ t continue_else
+ b try_brace
+:continue_else
+ N
+:try_brace
+ s/^\([\t ].*\)\n[\t ][\t ]*{/\1 {/g
+ s/^\([^#()]*\)\n[\t ]*{/\1 {/g
+ t done_brace
+ P
+ D
+:done_brace
+ p
+ d
+}
diff --git a/src/VBox/Additions/linux/drm/vbox_drv.c b/src/VBox/Additions/linux/drm/vbox_drv.c
new file mode 100644
index 00000000..d6373843
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_drv.c
@@ -0,0 +1,341 @@
+/* $Id: vbox_drv.c $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ * This file is based on ast_drv.c
+ * Copyright 2012 Red Hat Inc.
+ *
+ * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Dave Airlie <airlied@redhat.com>
+ * Michael Thayer <michael.thayer@oracle.com,
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/vt_kern.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "vbox_drv.h"
+
+#include "version-generated.h"
+#include "revision-generated.h"
+
+static int vbox_modeset = -1;
+
+MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
+module_param_named(modeset, vbox_modeset, int, 0400);
+
+static struct drm_driver driver;
+
+static const struct pci_device_id pciidlist[] = {
+ { 0x80ee, 0xbeef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0, 0, 0},
+};
+MODULE_DEVICE_TABLE(pci, pciidlist);
+
+static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ return drm_get_pci_dev(pdev, ent, &driver);
+}
+
+static void vbox_pci_remove(struct pci_dev *pdev)
+{
+ struct drm_device *dev = pci_get_drvdata(pdev);
+
+ drm_put_dev(dev);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) && !defined(RHEL_74)
+static void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
+ bool suspend)
+{
+ if (!fb_helper || !fb_helper->fbdev)
+ return;
+
+ console_lock();
+ fb_set_suspend(fb_helper->fbdev, suspend);
+ console_unlock();
+}
+#endif
+
+static int vbox_drm_freeze(struct drm_device *dev)
+{
+ struct vbox_private *vbox = dev->dev_private;
+
+ drm_kms_helper_poll_disable(dev);
+
+ pci_save_state(dev->pdev);
+
+ drm_fb_helper_set_suspend_unlocked(&vbox->fbdev->helper, true);
+
+ return 0;
+}
+
+static int vbox_drm_thaw(struct drm_device *dev)
+{
+ struct vbox_private *vbox = dev->dev_private;
+
+ drm_mode_config_reset(dev);
+ drm_helper_resume_force_mode(dev);
+ drm_fb_helper_set_suspend_unlocked(&vbox->fbdev->helper, false);
+
+ return 0;
+}
+
+static int vbox_drm_resume(struct drm_device *dev)
+{
+ int ret;
+
+ if (pci_enable_device(dev->pdev))
+ return -EIO;
+
+ ret = vbox_drm_thaw(dev);
+ if (ret)
+ return ret;
+
+ drm_kms_helper_poll_enable(dev);
+
+ return 0;
+}
+
+static int vbox_pm_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *ddev = pci_get_drvdata(pdev);
+ int error;
+
+ error = vbox_drm_freeze(ddev);
+ if (error)
+ return error;
+
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+
+ return 0;
+}
+
+static int vbox_pm_resume(struct device *dev)
+{
+ struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+
+ return vbox_drm_resume(ddev);
+}
+
+static int vbox_pm_freeze(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *ddev = pci_get_drvdata(pdev);
+
+ if (!ddev || !ddev->dev_private)
+ return -ENODEV;
+
+ return vbox_drm_freeze(ddev);
+}
+
+static int vbox_pm_thaw(struct device *dev)
+{
+ struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+
+ return vbox_drm_thaw(ddev);
+}
+
+static int vbox_pm_poweroff(struct device *dev)
+{
+ struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+
+ return vbox_drm_freeze(ddev);
+}
+
+static const struct dev_pm_ops vbox_pm_ops = {
+ .suspend = vbox_pm_suspend,
+ .resume = vbox_pm_resume,
+ .freeze = vbox_pm_freeze,
+ .thaw = vbox_pm_thaw,
+ .poweroff = vbox_pm_poweroff,
+ .restore = vbox_pm_resume,
+};
+
+static struct pci_driver vbox_pci_driver = {
+ .name = DRIVER_NAME,
+ .id_table = pciidlist,
+ .probe = vbox_pci_probe,
+ .remove = vbox_pci_remove,
+ .driver.pm = &vbox_pm_ops,
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) && !defined(RHEL_74)
+/* This works around a bug in X servers prior to 1.18.4, which sometimes
+ * submit more dirty rectangles than the kernel is willing to handle and
+ * then disable dirty rectangle handling altogether when they see the
+ * EINVAL error. I do not want the code to hang around forever, which is
+ * why I am limiting it to certain kernel versions. We can increase the
+ * limit if some distributions uses old X servers with new kernels. */
+long vbox_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ long rc = drm_ioctl(filp, cmd, arg);
+
+ if (cmd == DRM_IOCTL_MODE_DIRTYFB && rc == -EINVAL)
+ return -EOVERFLOW;
+
+ return rc;
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) && !RHEL_74 */
+
+static const struct file_operations vbox_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) && !defined(RHEL_74)
+ .unlocked_ioctl = vbox_ioctl,
+#else
+ .unlocked_ioctl = drm_ioctl,
+#endif
+ .mmap = vbox_mmap,
+ .poll = drm_poll,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) && !defined(RHEL_70)
+ .fasync = drm_fasync,
+#endif
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .read = drm_read,
+};
+
+static int vbox_master_set(struct drm_device *dev,
+ struct drm_file *file_priv, bool from_open)
+{
+ struct vbox_private *vbox = dev->dev_private;
+
+ /*
+ * We do not yet know whether the new owner can handle hotplug, so we
+ * do not advertise dynamic modes on the first query and send a
+ * tentative hotplug notification after that to see if they query again.
+ */
+ vbox->initial_mode_queried = false;
+
+ mutex_lock(&vbox->hw_mutex);
+ /* Start the refresh timer in case the user does not provide dirty
+ * rectangles. */
+ vbox->need_refresh_timer = true;
+ schedule_delayed_work(&vbox->refresh_work, VBOX_REFRESH_PERIOD);
+ mutex_unlock(&vbox->hw_mutex);
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) && !defined(RHEL_74)
+static void vbox_master_drop(struct drm_device *dev,
+ struct drm_file *file_priv, bool from_release)
+#else
+static void vbox_master_drop(struct drm_device *dev, struct drm_file *file_priv)
+#endif
+{
+ struct vbox_private *vbox = dev->dev_private;
+
+ /* See vbox_master_set() */
+ vbox->initial_mode_queried = false;
+ vbox_report_caps(vbox);
+
+ mutex_lock(&vbox->hw_mutex);
+ vbox->need_refresh_timer = false;
+ mutex_unlock(&vbox->hw_mutex);
+}
+
+static struct drm_driver driver = {
+ .driver_features =
+ DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED |
+ DRIVER_PRIME,
+ .dev_priv_size = 0,
+
+ .load = vbox_driver_load,
+ .unload = vbox_driver_unload,
+ .lastclose = vbox_driver_lastclose,
+ .master_set = vbox_master_set,
+ .master_drop = vbox_master_drop,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) || defined(RHEL_73)
+# if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) && !defined(RHEL_75)
+ .set_busid = drm_pci_set_busid,
+# endif
+#endif
+
+ .fops = &vbox_fops,
+ .irq_handler = vbox_irq_handler,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = DRIVER_PATCHLEVEL,
+
+ .gem_free_object = vbox_gem_free_object,
+ .dumb_create = vbox_dumb_create,
+ .dumb_map_offset = vbox_dumb_mmap_offset,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) && !defined(RHEL_73)
+ .dumb_destroy = vbox_dumb_destroy,
+#else
+ .dumb_destroy = drm_gem_dumb_destroy,
+#endif
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_pin = vbox_gem_prime_pin,
+ .gem_prime_unpin = vbox_gem_prime_unpin,
+ .gem_prime_get_sg_table = vbox_gem_prime_get_sg_table,
+ .gem_prime_import_sg_table = vbox_gem_prime_import_sg_table,
+ .gem_prime_vmap = vbox_gem_prime_vmap,
+ .gem_prime_vunmap = vbox_gem_prime_vunmap,
+ .gem_prime_mmap = vbox_gem_prime_mmap,
+};
+
+static int __init vbox_init(void)
+{
+#if defined(CONFIG_VGA_CONSOLE) || LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
+ if (vgacon_text_force() && vbox_modeset == -1)
+ return -EINVAL;
+#endif
+
+ if (vbox_modeset == 0)
+ return -EINVAL;
+
+ return pci_register_driver(&vbox_pci_driver);
+}
+
+static void __exit vbox_exit(void)
+{
+ pci_unregister_driver(&vbox_pci_driver);
+}
+
+module_init(vbox_init);
+module_exit(vbox_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL and additional rights");
+#ifdef MODULE_VERSION
+MODULE_VERSION(VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV));
+#endif
diff --git a/src/VBox/Additions/linux/drm/vbox_drv.h b/src/VBox/Additions/linux/drm/vbox_drv.h
new file mode 100644
index 00000000..59bbe261
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_drv.h
@@ -0,0 +1,425 @@
+/* $Id: vbox_drv.h $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ * This file is based on ast_drv.h
+ * Copyright 2012 Red Hat Inc.
+ *
+ * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Dave Airlie <airlied@redhat.com>
+ * Michael Thayer <michael.thayer@oracle.com,
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#ifndef GA_INCLUDED_SRC_linux_drm_vbox_drv_h
+#define GA_INCLUDED_SRC_linux_drm_vbox_drv_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)
+# include <linux/types.h>
+# include <linux/spinlock_types.h>
+#endif
+
+#include <linux/genalloc.h>
+#include <linux/io.h>
+#include <linux/string.h>
+
+#if defined(RHEL_MAJOR) && defined(RHEL_MINOR)
+# if RHEL_MAJOR == 7 && RHEL_MINOR >= 6
+# define RHEL_76
+# endif
+# if RHEL_MAJOR == 7 && RHEL_MINOR >= 5
+# define RHEL_75
+# endif
+# if RHEL_MAJOR == 7 && RHEL_MINOR >= 4
+# define RHEL_74
+# endif
+# if RHEL_MAJOR == 7 && RHEL_MINOR >= 3
+# define RHEL_73
+# endif
+# if RHEL_MAJOR == 7 && RHEL_MINOR >= 2
+# define RHEL_72
+# endif
+# if RHEL_MAJOR == 7 && RHEL_MINOR >= 1
+# define RHEL_71
+# endif
+# if RHEL_MAJOR == 7 && RHEL_MINOR >= 0
+# define RHEL_70
+# endif
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) || defined(RHEL_71)
+#define U8_MAX ((u8)~0U)
+#define S8_MAX ((s8)(U8_MAX>>1))
+#define S8_MIN ((s8)(-S8_MAX - 1))
+#define U16_MAX ((u16)~0U)
+#define S16_MAX ((s16)(U16_MAX>>1))
+#define S16_MIN ((s16)(-S16_MAX - 1))
+#define U32_MAX ((u32)~0U)
+#define S32_MAX ((s32)(U32_MAX>>1))
+#define S32_MIN ((s32)(-S32_MAX - 1))
+#define U64_MAX ((u64)~0ULL)
+#define S64_MAX ((s64)(U64_MAX>>1))
+#define S64_MIN ((s64)(-S64_MAX - 1))
+#endif
+
+#include <drm/drmP.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) || defined(RHEL_75)
+#include <drm/drm_encoder.h>
+#endif
+#include <drm/drm_fb_helper.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) || defined(RHEL_72)
+#include <drm/drm_gem.h>
+#endif
+
+#include <drm/ttm/ttm_bo_api.h>
+#include <drm/ttm/ttm_bo_driver.h>
+#include <drm/ttm/ttm_placement.h>
+#include <drm/ttm/ttm_memory.h>
+#include <drm/ttm/ttm_module.h>
+
+#include "vboxvideo_guest.h"
+#include "vboxvideo_vbe.h"
+#include "hgsmi_ch_setup.h"
+
+#include "product-generated.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) && !defined(RHEL_75)
+static inline void drm_gem_object_put_unlocked(struct drm_gem_object *obj)
+{
+ drm_gem_object_unreference_unlocked(obj);
+}
+#endif
+
+#define DRIVER_AUTHOR VBOX_VENDOR
+
+#define DRIVER_NAME "vboxvideo"
+#define DRIVER_DESC VBOX_PRODUCT " Graphics Card"
+#define DRIVER_DATE "20130823"
+
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 0
+
+#define VBOX_MAX_CURSOR_WIDTH 64
+#define VBOX_MAX_CURSOR_HEIGHT 64
+#define CURSOR_PIXEL_COUNT (VBOX_MAX_CURSOR_WIDTH * VBOX_MAX_CURSOR_HEIGHT)
+#define CURSOR_DATA_SIZE (CURSOR_PIXEL_COUNT * 4 + CURSOR_PIXEL_COUNT / 8)
+
+#define VBOX_MAX_SCREENS 32
+
+#define GUEST_HEAP_OFFSET(vbox) ((vbox)->full_vram_size - \
+ VBVA_ADAPTER_INFORMATION_SIZE)
+#define GUEST_HEAP_SIZE VBVA_ADAPTER_INFORMATION_SIZE
+#define GUEST_HEAP_USABLE_SIZE (VBVA_ADAPTER_INFORMATION_SIZE - \
+ sizeof(HGSMIHOSTFLAGS))
+#define HOST_FLAGS_OFFSET GUEST_HEAP_USABLE_SIZE
+
+/** How frequently we refresh if the guest is not providing dirty rectangles. */
+#define VBOX_REFRESH_PERIOD (HZ / 2)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) && !defined(RHEL_72)
+static inline void *devm_kcalloc(struct device *dev, size_t n, size_t size,
+ gfp_t flags)
+{
+ return devm_kzalloc(dev, n * size, flags);
+}
+#endif
+
+struct vbox_fbdev;
+
+struct vbox_private {
+ struct drm_device *dev;
+
+ u8 __iomem *guest_heap;
+ u8 __iomem *vbva_buffers;
+ struct gen_pool *guest_pool;
+ struct VBVABUFFERCONTEXT *vbva_info;
+ bool any_pitch;
+ u32 num_crtcs;
+ /** Amount of available VRAM, including space used for buffers. */
+ u32 full_vram_size;
+ /** Amount of available VRAM, not including space used for buffers. */
+ u32 available_vram_size;
+ /** Array of structures for receiving mode hints. */
+ VBVAMODEHINT *last_mode_hints;
+
+ struct vbox_fbdev *fbdev;
+
+ int fb_mtrr;
+
+ struct {
+ struct drm_global_reference mem_global_ref;
+ struct ttm_bo_global_ref bo_global_ref;
+ struct ttm_bo_device bdev;
+ bool mm_initialised;
+ } ttm;
+
+ struct mutex hw_mutex; /* protects modeset and accel/vbva accesses */
+ /**
+ * We decide whether or not user-space supports display hot-plug
+ * depending on whether they react to a hot-plug event after the initial
+ * mode query.
+ */
+ bool initial_mode_queried;
+ /**
+ * Do we know that the current user can send us dirty rectangle information?
+ * If not, do periodic refreshes until we do know.
+ */
+ bool need_refresh_timer;
+ /**
+ * As long as the user is not sending us dirty rectangle information,
+ * refresh the whole screen at regular intervals.
+ */
+ struct delayed_work refresh_work;
+ struct work_struct hotplug_work;
+ u32 input_mapping_width;
+ u32 input_mapping_height;
+ /**
+ * Is user-space using an X.Org-style layout of one large frame-buffer
+ * encompassing all screen ones or is the fbdev console active?
+ */
+ bool single_framebuffer;
+ u32 cursor_width;
+ u32 cursor_height;
+ u32 cursor_hot_x;
+ u32 cursor_hot_y;
+ size_t cursor_data_size;
+ u8 cursor_data[CURSOR_DATA_SIZE];
+};
+
+#undef CURSOR_PIXEL_COUNT
+#undef CURSOR_DATA_SIZE
+
+int vbox_driver_load(struct drm_device *dev, unsigned long flags);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) || defined(RHEL_75)
+void vbox_driver_unload(struct drm_device *dev);
+#else
+int vbox_driver_unload(struct drm_device *dev);
+#endif
+void vbox_driver_lastclose(struct drm_device *dev);
+
+struct vbox_gem_object;
+
+#ifndef VGA_PORT_HGSMI_HOST
+#define VGA_PORT_HGSMI_HOST 0x3b0
+#define VGA_PORT_HGSMI_GUEST 0x3d0
+#endif
+
+struct vbox_connector {
+ struct drm_connector base;
+ char name[32];
+ struct vbox_crtc *vbox_crtc;
+ struct {
+ u16 width;
+ u16 height;
+ bool disconnected;
+ } mode_hint;
+};
+
+struct vbox_crtc {
+ struct drm_crtc base;
+ bool blanked;
+ bool disconnected;
+ unsigned int crtc_id;
+ u32 fb_offset;
+ bool cursor_enabled;
+ u32 x_hint;
+ u32 y_hint;
+};
+
+struct vbox_encoder {
+ struct drm_encoder base;
+};
+
+struct vbox_framebuffer {
+ struct drm_framebuffer base;
+ struct drm_gem_object *obj;
+};
+
+struct vbox_fbdev {
+ struct drm_fb_helper helper;
+ struct vbox_framebuffer afb;
+ int size;
+ struct ttm_bo_kmap_obj mapping;
+ int x1, y1, x2, y2; /* dirty rect */
+ spinlock_t dirty_lock;
+};
+
+#define to_vbox_crtc(x) container_of(x, struct vbox_crtc, base)
+#define to_vbox_connector(x) container_of(x, struct vbox_connector, base)
+#define to_vbox_encoder(x) container_of(x, struct vbox_encoder, base)
+#define to_vbox_framebuffer(x) container_of(x, struct vbox_framebuffer, base)
+
+int vbox_mode_init(struct drm_device *dev);
+void vbox_mode_fini(struct drm_device *dev);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+#define DRM_MODE_FB_CMD drm_mode_fb_cmd
+#else
+#define DRM_MODE_FB_CMD drm_mode_fb_cmd2
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) && !defined(RHEL_71)
+#define CRTC_FB(crtc) ((crtc)->fb)
+#else
+#define CRTC_FB(crtc) ((crtc)->primary->fb)
+#endif
+
+void vbox_enable_accel(struct vbox_private *vbox);
+void vbox_disable_accel(struct vbox_private *vbox);
+void vbox_report_caps(struct vbox_private *vbox);
+
+void vbox_framebuffer_dirty_rectangles(struct drm_framebuffer *fb,
+ struct drm_clip_rect *rects,
+ unsigned int num_rects);
+
+int vbox_framebuffer_init(struct drm_device *dev,
+ struct vbox_framebuffer *vbox_fb,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) || defined(RHEL_73)
+ const struct DRM_MODE_FB_CMD *mode_cmd,
+#else
+ struct DRM_MODE_FB_CMD *mode_cmd,
+#endif
+ struct drm_gem_object *obj);
+
+int vbox_fbdev_init(struct drm_device *dev);
+void vbox_fbdev_fini(struct drm_device *dev);
+void vbox_fbdev_set_base(struct vbox_private *vbox, unsigned long gpu_addr);
+
+struct vbox_bo {
+ struct ttm_buffer_object bo;
+ struct ttm_placement placement;
+ struct ttm_bo_kmap_obj kmap;
+ struct drm_gem_object gem;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) && !defined(RHEL_72)
+ u32 placements[3];
+#else
+ struct ttm_place placements[3];
+#endif
+ int pin_count;
+};
+
+#define gem_to_vbox_bo(gobj) container_of((gobj), struct vbox_bo, gem)
+
+static inline struct vbox_bo *vbox_bo(struct ttm_buffer_object *bo)
+{
+ return container_of(bo, struct vbox_bo, bo);
+}
+
+#define to_vbox_obj(x) container_of(x, struct vbox_gem_object, base)
+
+int vbox_dumb_create(struct drm_file *file,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) && !defined(RHEL_73)
+int vbox_dumb_destroy(struct drm_file *file,
+ struct drm_device *dev, u32 handle);
+#endif
+
+void vbox_gem_free_object(struct drm_gem_object *obj);
+int vbox_dumb_mmap_offset(struct drm_file *file,
+ struct drm_device *dev,
+ u32 handle, u64 *offset);
+
+#define DRM_FILE_PAGE_OFFSET (0x10000000ULL >> PAGE_SHIFT)
+
+int vbox_mm_init(struct vbox_private *vbox);
+void vbox_mm_fini(struct vbox_private *vbox);
+
+int vbox_bo_create(struct drm_device *dev, int size, int align,
+ u32 flags, struct vbox_bo **pvboxbo);
+
+int vbox_gem_create(struct drm_device *dev,
+ u32 size, bool iskernel, struct drm_gem_object **obj);
+
+int vbox_bo_pin(struct vbox_bo *bo, u32 pl_flag, u64 *gpu_addr);
+int vbox_bo_unpin(struct vbox_bo *bo);
+
+static inline int vbox_bo_reserve(struct vbox_bo *bo, bool no_wait)
+{
+ int ret;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) || defined(RHEL_74)
+ ret = ttm_bo_reserve(&bo->bo, true, no_wait, NULL);
+#else
+ ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
+#endif
+ if (ret) {
+ if (ret != -ERESTARTSYS && ret != -EBUSY)
+ DRM_ERROR("reserve failed %p\n", bo);
+ return ret;
+ }
+ return 0;
+}
+
+static inline void vbox_bo_unreserve(struct vbox_bo *bo)
+{
+ ttm_bo_unreserve(&bo->bo);
+}
+
+void vbox_ttm_placement(struct vbox_bo *bo, int domain);
+int vbox_bo_push_sysram(struct vbox_bo *bo);
+int vbox_mmap(struct file *filp, struct vm_area_struct *vma);
+
+/* vbox_prime.c */
+int vbox_gem_prime_pin(struct drm_gem_object *obj);
+void vbox_gem_prime_unpin(struct drm_gem_object *obj);
+struct sg_table *vbox_gem_prime_get_sg_table(struct drm_gem_object *obj);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) && !defined(RHEL_72)
+struct drm_gem_object *vbox_gem_prime_import_sg_table(
+ struct drm_device *dev, size_t size, struct sg_table *table);
+#else
+struct drm_gem_object *vbox_gem_prime_import_sg_table(
+ struct drm_device *dev, struct dma_buf_attachment *attach,
+ struct sg_table *table);
+#endif
+void *vbox_gem_prime_vmap(struct drm_gem_object *obj);
+void vbox_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+int vbox_gem_prime_mmap(struct drm_gem_object *obj,
+ struct vm_area_struct *area);
+
+/* vbox_irq.c */
+int vbox_irq_init(struct vbox_private *vbox);
+void vbox_irq_fini(struct vbox_private *vbox);
+void vbox_report_hotplug(struct vbox_private *vbox);
+irqreturn_t vbox_irq_handler(int irq, void *arg);
+
+/* vbox_hgsmi.c */
+void *hgsmi_buffer_alloc(struct gen_pool *guest_pool, size_t size,
+ u8 channel, u16 channel_info);
+void hgsmi_buffer_free(struct gen_pool *guest_pool, void *buf);
+int hgsmi_buffer_submit(struct gen_pool *guest_pool, void *buf);
+
+static inline void vbox_write_ioport(u16 index, u16 data)
+{
+ outw(index, VBE_DISPI_IOPORT_INDEX);
+ outw(data, VBE_DISPI_IOPORT_DATA);
+}
+
+#endif /* !GA_INCLUDED_SRC_linux_drm_vbox_drv_h */
diff --git a/src/VBox/Additions/linux/drm/vbox_fb.c b/src/VBox/Additions/linux/drm/vbox_fb.c
new file mode 100644
index 00000000..6c9d3c0a
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_fb.c
@@ -0,0 +1,458 @@
+/* $Id: vbox_fb.c $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ * This file is based on ast_fb.c
+ * Copyright 2012 Red Hat Inc.
+ *
+ * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Dave Airlie <airlied@redhat.com>
+ * Michael Thayer <michael.thayer@oracle.com,
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/sysrq.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "vbox_drv.h"
+#include <VBoxVideo.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) && !defined(RHEL_74)
+/**
+ * Tell the host about dirty rectangles to update.
+ */
+static void vbox_dirty_update(struct vbox_fbdev *fbdev,
+ int x, int y, int width, int height)
+{
+ struct drm_gem_object *obj;
+ struct vbox_bo *bo;
+ int ret = -EBUSY;
+ bool store_for_later = false;
+ int x2, y2;
+ unsigned long flags;
+ struct drm_clip_rect rect;
+
+ obj = fbdev->afb.obj;
+ bo = gem_to_vbox_bo(obj);
+
+ /*
+ * try and reserve the BO, if we fail with busy
+ * then the BO is being moved and we should
+ * store up the damage until later.
+ */
+ if (drm_can_sleep())
+ ret = vbox_bo_reserve(bo, true);
+ if (ret) {
+ if (ret != -EBUSY)
+ return;
+
+ store_for_later = true;
+ }
+
+ x2 = x + width - 1;
+ y2 = y + height - 1;
+ spin_lock_irqsave(&fbdev->dirty_lock, flags);
+
+ if (fbdev->y1 < y)
+ y = fbdev->y1;
+ if (fbdev->y2 > y2)
+ y2 = fbdev->y2;
+ if (fbdev->x1 < x)
+ x = fbdev->x1;
+ if (fbdev->x2 > x2)
+ x2 = fbdev->x2;
+
+ if (store_for_later) {
+ fbdev->x1 = x;
+ fbdev->x2 = x2;
+ fbdev->y1 = y;
+ fbdev->y2 = y2;
+ spin_unlock_irqrestore(&fbdev->dirty_lock, flags);
+ return;
+ }
+
+ fbdev->x1 = INT_MAX;
+ fbdev->y1 = INT_MAX;
+ fbdev->x2 = 0;
+ fbdev->y2 = 0;
+
+ spin_unlock_irqrestore(&fbdev->dirty_lock, flags);
+
+ /*
+ * Not sure why the original code subtracted 1 here, but I will keep
+ * it that way to avoid unnecessary differences.
+ */
+ rect.x1 = x;
+ rect.x2 = x2 + 1;
+ rect.y1 = y;
+ rect.y2 = y2 + 1;
+ vbox_framebuffer_dirty_rectangles(&fbdev->afb.base, &rect, 1);
+
+ vbox_bo_unreserve(bo);
+}
+#endif
+
+#ifdef CONFIG_FB_DEFERRED_IO
+# if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) && !defined(RHEL_74)
+static void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagelist)
+{
+ struct vbox_fbdev *fbdev = info->par;
+ unsigned long start, end, min, max;
+ struct page *page;
+ int y1, y2;
+
+ min = ULONG_MAX;
+ max = 0;
+ list_for_each_entry(page, pagelist, lru) {
+ start = page->index << PAGE_SHIFT;
+ end = start + PAGE_SIZE - 1;
+ min = min(min, start);
+ max = max(max, end);
+ }
+
+ if (min < max) {
+ y1 = min / info->fix.line_length;
+ y2 = (max / info->fix.line_length) + 1;
+ DRM_INFO("%s: Calling dirty update: 0, %d, %d, %d\n",
+ __func__, y1, info->var.xres, y2 - y1 - 1);
+ vbox_dirty_update(fbdev, 0, y1, info->var.xres, y2 - y1 - 1);
+ }
+}
+# endif
+
+static struct fb_deferred_io vbox_defio = {
+ .delay = HZ / 30,
+ .deferred_io = drm_fb_helper_deferred_io,
+};
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) && !defined(RHEL_73)
+static void drm_fb_helper_sys_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+ struct vbox_fbdev *fbdev = info->par;
+
+ sys_fillrect(info, rect);
+ vbox_dirty_update(fbdev, rect->dx, rect->dy, rect->width, rect->height);
+}
+
+static void drm_fb_helper_sys_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+ struct vbox_fbdev *fbdev = info->par;
+
+ sys_copyarea(info, area);
+ vbox_dirty_update(fbdev, area->dx, area->dy, area->width, area->height);
+}
+
+static void drm_fb_helper_sys_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ struct vbox_fbdev *fbdev = info->par;
+
+ sys_imageblit(info, image);
+ vbox_dirty_update(fbdev, image->dx, image->dy, image->width,
+ image->height);
+}
+#endif
+
+static struct fb_ops vboxfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = drm_fb_helper_check_var,
+ .fb_set_par = drm_fb_helper_set_par,
+ .fb_fillrect = drm_fb_helper_sys_fillrect,
+ .fb_copyarea = drm_fb_helper_sys_copyarea,
+ .fb_imageblit = drm_fb_helper_sys_imageblit,
+ .fb_pan_display = drm_fb_helper_pan_display,
+ .fb_blank = drm_fb_helper_blank,
+ .fb_setcmap = drm_fb_helper_setcmap,
+ .fb_debug_enter = drm_fb_helper_debug_enter,
+ .fb_debug_leave = drm_fb_helper_debug_leave,
+};
+
+static int vboxfb_create_object(struct vbox_fbdev *fbdev,
+ struct DRM_MODE_FB_CMD *mode_cmd,
+ struct drm_gem_object **gobj_p)
+{
+ struct drm_device *dev = fbdev->helper.dev;
+ u32 size;
+ struct drm_gem_object *gobj;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ u32 pitch = mode_cmd->pitch;
+#else
+ u32 pitch = mode_cmd->pitches[0];
+#endif
+
+ int ret;
+
+ size = pitch * mode_cmd->height;
+ ret = vbox_gem_create(dev, size, true, &gobj);
+ if (ret)
+ return ret;
+
+ *gobj_p = gobj;
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) && !defined(RHEL_73)
+static struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *helper)
+{
+ struct fb_info *info;
+ struct vbox_fbdev *fbdev =
+ container_of(helper, struct vbox_fbdev, helper);
+ struct drm_device *dev = fbdev->helper.dev;
+ struct device *device = &dev->pdev->dev;
+
+ info = framebuffer_alloc(0, device);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+ fbdev->helper.fbdev = info;
+
+ if (fb_alloc_cmap(&info->cmap, 256, 0))
+ return ERR_PTR(-ENOMEM);
+
+ info->apertures = alloc_apertures(1);
+ if (!info->apertures)
+ return ERR_PTR(-ENOMEM);
+
+ return info;
+}
+#endif
+
+static int vboxfb_create(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct vbox_fbdev *fbdev =
+ container_of(helper, struct vbox_fbdev, helper);
+ struct drm_device *dev = fbdev->helper.dev;
+ struct DRM_MODE_FB_CMD mode_cmd;
+ struct drm_framebuffer *fb;
+ struct fb_info *info;
+ struct drm_gem_object *gobj;
+ struct vbox_bo *bo;
+ int size, ret;
+ u32 pitch;
+
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ pitch = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ mode_cmd.bpp = sizes->surface_bpp;
+ mode_cmd.depth = sizes->surface_depth;
+ mode_cmd.pitch = pitch;
+#else
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+ mode_cmd.pitches[0] = pitch;
+#endif
+
+ size = pitch * mode_cmd.height;
+
+ ret = vboxfb_create_object(fbdev, &mode_cmd, &gobj);
+ if (ret) {
+ DRM_ERROR("failed to create fbcon backing object %d\n", ret);
+ return ret;
+ }
+
+ ret = vbox_framebuffer_init(dev, &fbdev->afb, &mode_cmd, gobj);
+ if (ret)
+ return ret;
+
+ bo = gem_to_vbox_bo(gobj);
+
+ ret = vbox_bo_reserve(bo, false);
+ if (ret)
+ return ret;
+
+ ret = vbox_bo_pin(bo, TTM_PL_FLAG_VRAM, NULL);
+ if (ret) {
+ vbox_bo_unreserve(bo);
+ return ret;
+ }
+
+ ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
+ vbox_bo_unreserve(bo);
+ if (ret) {
+ DRM_ERROR("failed to kmap fbcon\n");
+ return ret;
+ }
+
+ info = drm_fb_helper_alloc_fbi(helper);
+ if (IS_ERR(info))
+ return -PTR_ERR(info);
+
+ info->par = fbdev;
+
+ fbdev->size = size;
+
+ fb = &fbdev->afb.base;
+ fbdev->helper.fb = fb;
+
+ strcpy(info->fix.id, "vboxdrmfb");
+
+ /*
+ * The last flag forces a mode set on VT switches even if the kernel
+ * does not think it is needed.
+ */
+ info->flags = FBINFO_DEFAULT | FBINFO_MISC_ALWAYS_SETPAR;
+ info->fbops = &vboxfb_ops;
+
+ /*
+ * This seems to be done for safety checking that the framebuffer
+ * is not registered twice by different drivers.
+ */
+ info->apertures->ranges[0].base = pci_resource_start(dev->pdev, 0);
+ info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 0);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) || defined(RHEL_75)
+ drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth);
+#else
+ drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
+#endif
+ drm_fb_helper_fill_var(info, &fbdev->helper, sizes->fb_width,
+ sizes->fb_height);
+
+ info->screen_base = bo->kmap.virtual;
+ info->screen_size = size;
+
+#ifdef CONFIG_FB_DEFERRED_IO
+ info->fbdefio = &vbox_defio;
+ fb_deferred_io_init(info);
+#endif
+
+ info->pixmap.flags = FB_PIXMAP_SYSTEM;
+
+ DRM_DEBUG_KMS("allocated %dx%d\n", fb->width, fb->height);
+
+ return 0;
+}
+
+static struct drm_fb_helper_funcs vbox_fb_helper_funcs = {
+ .fb_probe = vboxfb_create,
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) && !defined(RHEL_73)
+static void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper)
+{
+ if (fb_helper && fb_helper->fbdev)
+ unregister_framebuffer(fb_helper->fbdev);
+}
+#endif
+
+void vbox_fbdev_fini(struct drm_device *dev)
+{
+ struct vbox_private *vbox = dev->dev_private;
+ struct vbox_fbdev *fbdev = vbox->fbdev;
+ struct vbox_framebuffer *afb = &fbdev->afb;
+
+ drm_fb_helper_unregister_fbi(&fbdev->helper);
+
+ if (afb->obj) {
+ struct vbox_bo *bo = gem_to_vbox_bo(afb->obj);
+
+ if (!vbox_bo_reserve(bo, false)) {
+ if (bo->kmap.virtual)
+ ttm_bo_kunmap(&bo->kmap);
+ /*
+ * QXL does this, but is it really needed before
+ * freeing?
+ */
+ if (bo->pin_count)
+ vbox_bo_unpin(bo);
+ vbox_bo_unreserve(bo);
+ }
+ drm_gem_object_put_unlocked(afb->obj);
+ afb->obj = NULL;
+ }
+ drm_fb_helper_fini(&fbdev->helper);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ drm_framebuffer_unregister_private(&afb->base);
+#endif
+ drm_framebuffer_cleanup(&afb->base);
+}
+
+int vbox_fbdev_init(struct drm_device *dev)
+{
+ struct vbox_private *vbox = dev->dev_private;
+ struct vbox_fbdev *fbdev;
+ int ret;
+
+ fbdev = devm_kzalloc(dev->dev, sizeof(*fbdev), GFP_KERNEL);
+ if (!fbdev)
+ return -ENOMEM;
+
+ vbox->fbdev = fbdev;
+ spin_lock_init(&fbdev->dirty_lock);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) && !defined(RHEL_73)
+ fbdev->helper.funcs = &vbox_fb_helper_funcs;
+#else
+ drm_fb_helper_prepare(dev, &fbdev->helper, &vbox_fb_helper_funcs);
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) || defined(RHEL_75)
+ ret = drm_fb_helper_init(dev, &fbdev->helper, vbox->num_crtcs);
+#else
+ ret =
+ drm_fb_helper_init(dev, &fbdev->helper, vbox->num_crtcs,
+ vbox->num_crtcs);
+#endif
+ if (ret)
+ return ret;
+
+ ret = drm_fb_helper_single_add_all_connectors(&fbdev->helper);
+ if (ret)
+ goto err_fini;
+
+ /* disable all the possible outputs/crtcs before entering KMS mode */
+ drm_helper_disable_unused_functions(dev);
+
+ ret = drm_fb_helper_initial_config(&fbdev->helper, 32);
+ if (ret)
+ goto err_fini;
+
+ return 0;
+
+err_fini:
+ drm_fb_helper_fini(&fbdev->helper);
+ return ret;
+}
+
+void vbox_fbdev_set_base(struct vbox_private *vbox, unsigned long gpu_addr)
+{
+ struct fb_info *fbdev = vbox->fbdev->helper.fbdev;
+
+ fbdev->fix.smem_start = fbdev->apertures->ranges[0].base + gpu_addr;
+ fbdev->fix.smem_len = vbox->available_vram_size - gpu_addr;
+}
diff --git a/src/VBox/Additions/linux/drm/vbox_hgsmi.c b/src/VBox/Additions/linux/drm/vbox_hgsmi.c
new file mode 100644
index 00000000..a92ee952
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_hgsmi.c
@@ -0,0 +1,130 @@
+/** @file
+ * VirtualBox Additions Linux kernel video driver hgsmi interface code
+ */
+
+/*
+ * Contributed by Hans de Goede <hdegoede@redhat.com>
+ *
+ * Copyright (C) 2017-2019 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.
+ */
+
+#include <HGSMIBase.h>
+#include <VBoxVideoVBE.h>
+
+/* One-at-a-Time Hash from http://www.burtleburtle.net/bob/hash/doobs.html */
+static u32 hgsmi_hash_process(u32 hash, const u8 *data, int size)
+{
+ while (size--) {
+ hash += *data++;
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+
+ return hash;
+}
+
+static u32 hgsmi_hash_end(u32 hash)
+{
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+
+ return hash;
+}
+
+/* Not really a checksum but that is the naming used in all vbox code */
+static u32 hgsmi_checksum(u32 offset,
+ const HGSMIBUFFERHEADER *header,
+ const HGSMIBUFFERTAIL *tail)
+{
+ u32 checksum;
+
+ checksum = hgsmi_hash_process(0, (u8 *)&offset, sizeof(offset));
+ checksum = hgsmi_hash_process(checksum, (u8 *)header, sizeof(*header));
+ /* 4 -> Do not checksum the checksum itself */
+ checksum = hgsmi_hash_process(checksum, (u8 *)tail, 4);
+
+ return hgsmi_hash_end(checksum);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+void *gen_pool_dma_alloc(struct gen_pool *pool, size_t size, dma_addr_t *dma)
+{
+ unsigned long vaddr = gen_pool_alloc(pool, size);
+
+ if (vaddr)
+ *dma = gen_pool_virt_to_phys(pool, vaddr);
+ return (void *)vaddr;
+}
+#endif
+
+void *hgsmi_buffer_alloc(struct gen_pool *guest_pool, size_t size,
+ u8 channel, u16 channel_info)
+{
+ HGSMIBUFFERHEADER *h;
+ HGSMIBUFFERTAIL *t;
+ size_t total_size;
+ dma_addr_t offset;
+
+ total_size = size + sizeof(*h) + sizeof(*t);
+ h = gen_pool_dma_alloc(guest_pool, total_size, &offset);
+ if (!h)
+ return NULL;
+
+ t = (HGSMIBUFFERTAIL *)((u8 *)h + sizeof(*h) + size);
+
+ h->u8Flags = HGSMI_BUFFER_HEADER_F_SEQ_SINGLE;
+ h->u32DataSize = size;
+ h->u8Channel = channel;
+ h->u16ChannelInfo = channel_info;
+ memset(&h->u.au8Union, 0, sizeof(h->u.au8Union));
+
+ t->u32Reserved = 0;
+ t->u32Checksum = hgsmi_checksum(offset, h, t);
+
+ return (u8 *)h + sizeof(*h);
+}
+
+void hgsmi_buffer_free(struct gen_pool *guest_pool, void *buf)
+{
+ HGSMIBUFFERHEADER *h =
+ (HGSMIBUFFERHEADER *)((u8 *)buf - sizeof(*h));
+ size_t total_size = h->u32DataSize + sizeof(*h) +
+ sizeof(HGSMIBUFFERTAIL);
+
+ gen_pool_free(guest_pool, (unsigned long)h, total_size);
+}
+
+int hgsmi_buffer_submit(struct gen_pool *guest_pool, void *buf)
+{
+ phys_addr_t offset;
+
+ offset = gen_pool_virt_to_phys(guest_pool, (unsigned long)buf -
+ sizeof(HGSMIBUFFERHEADER));
+ outl(offset, VGA_PORT_HGSMI_GUEST);
+ /* Make the compiler aware that the host has changed memory. */
+ mb();
+
+ return 0;
+}
diff --git a/src/VBox/Additions/linux/drm/vbox_irq.c b/src/VBox/Additions/linux/drm/vbox_irq.c
new file mode 100644
index 00000000..31abcc21
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_irq.c
@@ -0,0 +1,212 @@
+/* $Id: vbox_irq.c $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2016-2019 Oracle Corporation
+ * This file is based on qxl_irq.c
+ * Copyright 2013 Red Hat Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Dave Airlie
+ * Alon Levy
+ * Michael Thayer <michael.thayer@oracle.com,
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+#include "vbox_drv.h"
+
+#include <drm/drm_crtc_helper.h>
+#include <VBoxVideo.h>
+
+static void vbox_clear_irq(void)
+{
+ outl((u32)~0, VGA_PORT_HGSMI_HOST);
+}
+
+static u32 vbox_get_flags(struct vbox_private *vbox)
+{
+ return readl(vbox->guest_heap + HOST_FLAGS_OFFSET);
+}
+
+void vbox_report_hotplug(struct vbox_private *vbox)
+{
+ schedule_work(&vbox->hotplug_work);
+}
+
+irqreturn_t vbox_irq_handler(int irq, void *arg)
+{
+ struct drm_device *dev = (struct drm_device *)arg;
+ struct vbox_private *vbox = (struct vbox_private *)dev->dev_private;
+ u32 host_flags = vbox_get_flags(vbox);
+
+ if (!(host_flags & HGSMIHOSTFLAGS_IRQ))
+ return IRQ_NONE;
+
+ /*
+ * Due to a bug in the initial host implementation of hot-plug irqs,
+ * the hot-plug and cursor capability flags were never cleared.
+ * Fortunately we can tell when they would have been set by checking
+ * that the VSYNC flag is not set.
+ */
+ if (host_flags &
+ (HGSMIHOSTFLAGS_HOTPLUG | HGSMIHOSTFLAGS_CURSOR_CAPABILITIES) &&
+ !(host_flags & HGSMIHOSTFLAGS_VSYNC))
+ vbox_report_hotplug(vbox);
+
+ vbox_clear_irq();
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * Check that the position hints provided by the host are suitable for GNOME
+ * shell (i.e. all screens disjoint and hints for all enabled screens) and if
+ * not replace them with default ones. Providing valid hints improves the
+ * chances that we will get a known screen layout for pointer mapping.
+ */
+static void validate_or_set_position_hints(struct vbox_private *vbox)
+{
+ struct VBVAMODEHINT *hintsi, *hintsj;
+ bool valid = true;
+ u16 currentx = 0;
+ int i, j;
+
+ for (i = 0; i < vbox->num_crtcs; ++i) {
+ for (j = 0; j < i; ++j) {
+ hintsi = &vbox->last_mode_hints[i];
+ hintsj = &vbox->last_mode_hints[j];
+
+ if (hintsi->fEnabled && hintsj->fEnabled) {
+ if (hintsi->dx >= 0xffff ||
+ hintsi->dy >= 0xffff ||
+ hintsj->dx >= 0xffff ||
+ hintsj->dy >= 0xffff ||
+ (hintsi->dx <
+ hintsj->dx + (hintsj->cx & 0x8fff) &&
+ hintsi->dx + (hintsi->cx & 0x8fff) >
+ hintsj->dx) ||
+ (hintsi->dy <
+ hintsj->dy + (hintsj->cy & 0x8fff) &&
+ hintsi->dy + (hintsi->cy & 0x8fff) >
+ hintsj->dy))
+ valid = false;
+ }
+ }
+ }
+ if (!valid)
+ for (i = 0; i < vbox->num_crtcs; ++i) {
+ if (vbox->last_mode_hints[i].fEnabled) {
+ vbox->last_mode_hints[i].dx = currentx;
+ vbox->last_mode_hints[i].dy = 0;
+ currentx +=
+ vbox->last_mode_hints[i].cx & 0x8fff;
+ }
+ }
+}
+
+/**
+ * Query the host for the most recent video mode hints.
+ */
+static void vbox_update_mode_hints(struct vbox_private *vbox)
+{
+ struct drm_device *dev = vbox->dev;
+ struct drm_connector *connector;
+ struct vbox_connector *vbox_conn;
+ struct VBVAMODEHINT *hints;
+ u16 flags;
+ bool disconnected;
+ unsigned int crtc_id;
+ int ret;
+
+ ret = VBoxHGSMIGetModeHints(vbox->guest_pool, vbox->num_crtcs,
+ vbox->last_mode_hints);
+ if (ret) {
+ DRM_ERROR("vboxvideo: hgsmi_get_mode_hints failed: %d\n", ret);
+ return;
+ }
+
+ validate_or_set_position_hints(vbox);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ drm_modeset_lock_all(dev);
+#else
+ mutex_lock(&dev->mode_config.mutex);
+#endif
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ vbox_conn = to_vbox_connector(connector);
+
+ hints = &vbox->last_mode_hints[vbox_conn->vbox_crtc->crtc_id];
+ if (hints->magic != VBVAMODEHINT_MAGIC)
+ continue;
+
+ disconnected = !(hints->fEnabled);
+ crtc_id = vbox_conn->vbox_crtc->crtc_id;
+ vbox_conn->mode_hint.width = hints->cx & 0x8fff;
+ vbox_conn->mode_hint.height = hints->cy & 0x8fff;
+ vbox_conn->vbox_crtc->x_hint = hints->dx;
+ vbox_conn->vbox_crtc->y_hint = hints->dy;
+ vbox_conn->mode_hint.disconnected = disconnected;
+
+ if (vbox_conn->vbox_crtc->disconnected == disconnected)
+ continue;
+
+ if (disconnected)
+ flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_DISABLED;
+ else
+ flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_BLANK;
+
+ VBoxHGSMIProcessDisplayInfo(vbox->guest_pool, crtc_id, 0, 0, 0,
+ hints->cx * 4, hints->cx,
+ hints->cy, 0, flags);
+
+ vbox_conn->vbox_crtc->disconnected = disconnected;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ drm_modeset_unlock_all(dev);
+#else
+ mutex_unlock(&dev->mode_config.mutex);
+#endif
+}
+
+static void vbox_hotplug_worker(struct work_struct *work)
+{
+ struct vbox_private *vbox = container_of(work, struct vbox_private,
+ hotplug_work);
+
+ vbox_update_mode_hints(vbox);
+ drm_kms_helper_hotplug_event(vbox->dev);
+}
+
+int vbox_irq_init(struct vbox_private *vbox)
+{
+ INIT_WORK(&vbox->hotplug_work, vbox_hotplug_worker);
+ vbox_update_mode_hints(vbox);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) || defined(RHEL_71)
+ return drm_irq_install(vbox->dev, vbox->dev->pdev->irq);
+#else
+ return drm_irq_install(vbox->dev);
+#endif
+}
+
+void vbox_irq_fini(struct vbox_private *vbox)
+{
+ drm_irq_uninstall(vbox->dev);
+ flush_work(&vbox->hotplug_work);
+}
diff --git a/src/VBox/Additions/linux/drm/vbox_main.c b/src/VBox/Additions/linux/drm/vbox_main.c
new file mode 100644
index 00000000..31029d70
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_main.c
@@ -0,0 +1,661 @@
+/* $Id: vbox_main.c $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ * This file is based on ast_main.c
+ * Copyright 2012 Red Hat Inc.
+ *
+ * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Dave Airlie <airlied@redhat.com>,
+ * Michael Thayer <michael.thayer@oracle.com,
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+#include "vbox_drv.h"
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include <VBoxVideoGuest.h>
+#include <VBoxVideoVBE.h>
+
+#include "hgsmi_channels.h"
+
+static void vbox_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ struct vbox_framebuffer *vbox_fb = to_vbox_framebuffer(fb);
+
+ if (vbox_fb->obj)
+ drm_gem_object_put_unlocked(vbox_fb->obj);
+
+ drm_framebuffer_cleanup(fb);
+ kfree(fb);
+}
+
+void vbox_enable_accel(struct vbox_private *vbox)
+{
+ unsigned int i;
+ struct VBVABUFFER *vbva;
+
+ if (!vbox->vbva_info || !vbox->vbva_buffers) {
+ /* Should never happen... */
+ DRM_ERROR("vboxvideo: failed to set up VBVA.\n");
+ return;
+ }
+
+ for (i = 0; i < vbox->num_crtcs; ++i) {
+ if (vbox->vbva_info[i].pVBVA)
+ continue;
+
+ vbva = (void *)vbox->vbva_buffers + i * VBVA_MIN_BUFFER_SIZE;
+ if (!VBoxVBVAEnable(&vbox->vbva_info[i],
+ vbox->guest_pool, vbva, i)) {
+ /* very old host or driver error. */
+ DRM_ERROR("vboxvideo: vbva_enable failed\n");
+ return;
+ }
+ }
+}
+
+void vbox_disable_accel(struct vbox_private *vbox)
+{
+ unsigned int i;
+
+ for (i = 0; i < vbox->num_crtcs; ++i)
+ VBoxVBVADisable(&vbox->vbva_info[i], vbox->guest_pool, i);
+}
+
+void vbox_report_caps(struct vbox_private *vbox)
+{
+ u32 caps = VBVACAPS_DISABLE_CURSOR_INTEGRATION |
+ VBVACAPS_IRQ | VBVACAPS_USE_VBVA_ONLY;
+
+ if (vbox->initial_mode_queried)
+ caps |= VBVACAPS_VIDEO_MODE_HINTS;
+
+ VBoxHGSMISendCapsInfo(vbox->guest_pool, caps);
+}
+
+/**
+ * Send information about dirty rectangles to VBVA. If necessary we enable
+ * VBVA first, as this is normally disabled after a change of master in case
+ * the new master does not send dirty rectangle information (is this even
+ * allowed?)
+ */
+void vbox_framebuffer_dirty_rectangles(struct drm_framebuffer *fb,
+ struct drm_clip_rect *rects,
+ unsigned int num_rects)
+{
+ struct vbox_private *vbox = fb->dev->dev_private;
+ struct drm_crtc *crtc;
+ unsigned int i;
+
+ /* The user can send rectangles, we do not need the timer. */
+ vbox->need_refresh_timer = false;
+ mutex_lock(&vbox->hw_mutex);
+ list_for_each_entry(crtc, &fb->dev->mode_config.crtc_list, head) {
+ if (CRTC_FB(crtc) != fb)
+ continue;
+
+ for (i = 0; i < num_rects; ++i) {
+ VBVACMDHDR cmd_hdr;
+ unsigned int crtc_id = to_vbox_crtc(crtc)->crtc_id;
+
+ if ((rects[i].x1 > crtc->x + crtc->hwmode.hdisplay) ||
+ (rects[i].y1 > crtc->y + crtc->hwmode.vdisplay) ||
+ (rects[i].x2 < crtc->x) ||
+ (rects[i].y2 < crtc->y))
+ continue;
+
+ cmd_hdr.x = (s16)rects[i].x1;
+ cmd_hdr.y = (s16)rects[i].y1;
+ cmd_hdr.w = (u16)rects[i].x2 - rects[i].x1;
+ cmd_hdr.h = (u16)rects[i].y2 - rects[i].y1;
+
+ if (!VBoxVBVABufferBeginUpdate(&vbox->vbva_info[crtc_id],
+ vbox->guest_pool))
+ continue;
+
+ VBoxVBVAWrite(&vbox->vbva_info[crtc_id], vbox->guest_pool,
+ &cmd_hdr, sizeof(cmd_hdr));
+ VBoxVBVABufferEndUpdate(&vbox->vbva_info[crtc_id]);
+ }
+ }
+ mutex_unlock(&vbox->hw_mutex);
+}
+
+static int vbox_user_framebuffer_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int flags, unsigned int color,
+ struct drm_clip_rect *rects,
+ unsigned int num_rects)
+{
+ vbox_framebuffer_dirty_rectangles(fb, rects, num_rects);
+
+ return 0;
+}
+
+static const struct drm_framebuffer_funcs vbox_fb_funcs = {
+ .destroy = vbox_user_framebuffer_destroy,
+ .dirty = vbox_user_framebuffer_dirty,
+};
+
+int vbox_framebuffer_init(struct drm_device *dev,
+ struct vbox_framebuffer *vbox_fb,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) || defined(RHEL_73)
+ const struct DRM_MODE_FB_CMD *mode_cmd,
+#else
+ struct DRM_MODE_FB_CMD *mode_cmd,
+#endif
+ struct drm_gem_object *obj)
+{
+ int ret;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) || defined(RHEL_75)
+ drm_helper_mode_fill_fb_struct(dev, &vbox_fb->base, mode_cmd);
+#else
+ drm_helper_mode_fill_fb_struct(&vbox_fb->base, mode_cmd);
+#endif
+ vbox_fb->obj = obj;
+ ret = drm_framebuffer_init(dev, &vbox_fb->base, &vbox_fb_funcs);
+ if (ret) {
+ DRM_ERROR("framebuffer init failed %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct drm_framebuffer *vbox_user_framebuffer_create(
+ struct drm_device *dev,
+ struct drm_file *filp,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) || defined(RHEL_73)
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+#else
+ struct drm_mode_fb_cmd2 *mode_cmd)
+#endif
+{
+ struct drm_gem_object *obj;
+ struct vbox_framebuffer *vbox_fb;
+ int ret = -ENOMEM;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) || defined(RHEL_74)
+ obj = drm_gem_object_lookup(filp, mode_cmd->handles[0]);
+#else
+ obj = drm_gem_object_lookup(dev, filp, mode_cmd->handles[0]);
+#endif
+ if (!obj)
+ return ERR_PTR(-ENOENT);
+
+ vbox_fb = kzalloc(sizeof(*vbox_fb), GFP_KERNEL);
+ if (!vbox_fb)
+ goto err_unref_obj;
+
+ ret = vbox_framebuffer_init(dev, vbox_fb, mode_cmd, obj);
+ if (ret)
+ goto err_free_vbox_fb;
+
+ return &vbox_fb->base;
+
+err_free_vbox_fb:
+ kfree(vbox_fb);
+err_unref_obj:
+ drm_gem_object_put_unlocked(obj);
+ return ERR_PTR(ret);
+}
+
+static const struct drm_mode_config_funcs vbox_mode_funcs = {
+ .fb_create = vbox_user_framebuffer_create,
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) && !defined(RHEL_73)
+#define pci_iomap_range(dev, bar, offset, maxlen) \
+ ioremap(pci_resource_start(dev, bar) + (offset), maxlen)
+#endif
+
+/**
+ * Tell the host about the views. This design originally targeted the
+ * Windows XP driver architecture and assumed that each screen would
+ * have a dedicated frame buffer with the command buffer following it,
+ * the whole being a "view". The host works out which screen a command
+ * buffer belongs to by checking whether it is in the first view, then
+ * whether it is in the second and so on. The first match wins. We
+ * cheat around this by making the first view be the managed memory
+ * plus the first command buffer, the second the same plus the second
+ * buffer and so on.
+ */
+static int vbox_set_views(struct vbox_private *vbox)
+{
+ VBVAINFOVIEW *p;
+ int i;
+
+ p = VBoxHGSMIBufferAlloc(vbox->guest_pool, sizeof(*p),
+ HGSMI_CH_VBVA, VBVA_INFO_VIEW);
+ if (!p)
+ return -ENOMEM;
+
+ for (i = 0; i < vbox->num_crtcs; ++i) {
+ p->u32ViewIndex = i;
+ p->u32ViewOffset = 0;
+ p->u32ViewSize = vbox->available_vram_size +
+ i * VBVA_MIN_BUFFER_SIZE;
+ p->u32MaxScreenSize = vbox->available_vram_size;
+
+ VBoxHGSMIBufferSubmit(vbox->guest_pool, p);
+ }
+
+ VBoxHGSMIBufferFree(vbox->guest_pool, p);
+
+ return 0;
+}
+
+static int vbox_accel_init(struct vbox_private *vbox)
+{
+ unsigned int i, ret;
+
+ vbox->vbva_info = devm_kcalloc(vbox->dev->dev, vbox->num_crtcs,
+ sizeof(*vbox->vbva_info), GFP_KERNEL);
+ if (!vbox->vbva_info)
+ return -ENOMEM;
+
+ /* Take a command buffer for each screen from the end of usable VRAM. */
+ vbox->available_vram_size -= vbox->num_crtcs * VBVA_MIN_BUFFER_SIZE;
+
+ vbox->vbva_buffers = pci_iomap_range(vbox->dev->pdev, 0,
+ vbox->available_vram_size,
+ vbox->num_crtcs *
+ VBVA_MIN_BUFFER_SIZE);
+ if (!vbox->vbva_buffers)
+ return -ENOMEM;
+
+ for (i = 0; i < vbox->num_crtcs; ++i)
+ VBoxVBVASetupBufferContext(&vbox->vbva_info[i],
+ vbox->available_vram_size +
+ i * VBVA_MIN_BUFFER_SIZE,
+ VBVA_MIN_BUFFER_SIZE);
+
+ vbox_enable_accel(vbox);
+ ret = vbox_set_views(vbox);
+ if (ret)
+ goto err_pci_iounmap;
+
+ return 0;
+
+err_pci_iounmap:
+ pci_iounmap(vbox->dev->pdev, vbox->vbva_buffers);
+ return ret;
+}
+
+static void vbox_accel_fini(struct vbox_private *vbox)
+{
+ vbox_disable_accel(vbox);
+ pci_iounmap(vbox->dev->pdev, vbox->vbva_buffers);
+}
+
+/** Do we support the 4.3 plus mode hint reporting interface? */
+static bool have_hgsmi_mode_hints(struct vbox_private *vbox)
+{
+ u32 have_hints, have_cursor;
+ int ret;
+
+ ret = VBoxQueryConfHGSMI(vbox->guest_pool,
+ VBOX_VBVA_CONF32_MODE_HINT_REPORTING,
+ &have_hints);
+ if (ret)
+ return false;
+
+ ret = VBoxQueryConfHGSMI(vbox->guest_pool,
+ VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING,
+ &have_cursor);
+ if (ret)
+ return false;
+
+ return have_hints == VINF_SUCCESS && have_cursor == VINF_SUCCESS;
+}
+
+/**
+ * Our refresh timer call-back. Only used for guests without dirty rectangle
+ * support.
+ */
+static void vbox_refresh_timer(struct work_struct *work)
+{
+ struct vbox_private *vbox = container_of(work, struct vbox_private,
+ refresh_work.work);
+ bool have_unblanked = false;
+ struct drm_crtc *crtci;
+
+ if (!vbox->need_refresh_timer)
+ return;
+ list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list, head) {
+ struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtci);
+ if (crtci->enabled && !vbox_crtc->blanked)
+ have_unblanked = true;
+ }
+ if (!have_unblanked)
+ return;
+ /* This forces a full refresh. */
+ vbox_enable_accel(vbox);
+ /* Schedule the next timer iteration. */
+ schedule_delayed_work(&vbox->refresh_work, VBOX_REFRESH_PERIOD);
+}
+
+static bool vbox_check_supported(u16 id)
+{
+ u16 dispi_id;
+
+ vbox_write_ioport(VBE_DISPI_INDEX_ID, id);
+ dispi_id = inw(VBE_DISPI_IOPORT_DATA);
+
+ return dispi_id == id;
+}
+
+/**
+ * Set up our heaps and data exchange buffers in VRAM before handing the rest
+ * to the memory manager.
+ */
+static int vbox_hw_init(struct vbox_private *vbox)
+{
+ int ret = -ENOMEM;
+
+ vbox->full_vram_size = inl(VBE_DISPI_IOPORT_DATA);
+ vbox->any_pitch = vbox_check_supported(VBE_DISPI_ID_ANYX);
+
+ DRM_INFO("VRAM %08x\n", vbox->full_vram_size);
+
+ /* Map guest-heap at end of vram */
+ vbox->guest_heap =
+ pci_iomap_range(vbox->dev->pdev, 0, GUEST_HEAP_OFFSET(vbox),
+ GUEST_HEAP_SIZE);
+ if (!vbox->guest_heap)
+ return -ENOMEM;
+
+ /* Create guest-heap mem-pool use 2^4 = 16 byte chunks */
+ vbox->guest_pool = gen_pool_create(4, -1);
+ if (!vbox->guest_pool)
+ goto err_unmap_guest_heap;
+
+ ret = gen_pool_add_virt(vbox->guest_pool,
+ (unsigned long)vbox->guest_heap,
+ GUEST_HEAP_OFFSET(vbox),
+ GUEST_HEAP_USABLE_SIZE, -1);
+ if (ret)
+ goto err_destroy_guest_pool;
+
+ /* Reduce available VRAM size to reflect the guest heap. */
+ vbox->available_vram_size = GUEST_HEAP_OFFSET(vbox);
+ /* Linux drm represents monitors as a 32-bit array. */
+ VBoxQueryConfHGSMI(vbox->guest_pool, VBOX_VBVA_CONF32_MONITOR_COUNT,
+ &vbox->num_crtcs);
+ vbox->num_crtcs = clamp_t(u32, vbox->num_crtcs, 1, VBOX_MAX_SCREENS);
+
+ if (!have_hgsmi_mode_hints(vbox)) {
+ ret = -ENOTSUPP;
+ goto err_destroy_guest_pool;
+ }
+
+ vbox->last_mode_hints = devm_kcalloc(vbox->dev->dev, vbox->num_crtcs,
+ sizeof(VBVAMODEHINT),
+ GFP_KERNEL);
+ if (!vbox->last_mode_hints) {
+ ret = -ENOMEM;
+ goto err_destroy_guest_pool;
+ }
+
+ ret = vbox_accel_init(vbox);
+ if (ret)
+ goto err_destroy_guest_pool;
+
+ /* Set up the refresh timer for users which do not send dirty rectangles. */
+ INIT_DELAYED_WORK(&vbox->refresh_work, vbox_refresh_timer);
+
+ return 0;
+
+err_destroy_guest_pool:
+ gen_pool_destroy(vbox->guest_pool);
+err_unmap_guest_heap:
+ pci_iounmap(vbox->dev->pdev, vbox->guest_heap);
+ return ret;
+}
+
+static void vbox_hw_fini(struct vbox_private *vbox)
+{
+ vbox->need_refresh_timer = false;
+ cancel_delayed_work(&vbox->refresh_work);
+ vbox_accel_fini(vbox);
+ gen_pool_destroy(vbox->guest_pool);
+ pci_iounmap(vbox->dev->pdev, vbox->guest_heap);
+}
+
+int vbox_driver_load(struct drm_device *dev, unsigned long flags)
+{
+ struct vbox_private *vbox;
+ int ret = 0;
+
+ if (!vbox_check_supported(VBE_DISPI_ID_HGSMI))
+ return -ENODEV;
+
+ vbox = devm_kzalloc(dev->dev, sizeof(*vbox), GFP_KERNEL);
+ if (!vbox)
+ return -ENOMEM;
+
+ dev->dev_private = vbox;
+ vbox->dev = dev;
+
+ mutex_init(&vbox->hw_mutex);
+
+ ret = vbox_hw_init(vbox);
+ if (ret)
+ return ret;
+
+ ret = vbox_mm_init(vbox);
+ if (ret)
+ goto err_hw_fini;
+
+ drm_mode_config_init(dev);
+
+ dev->mode_config.funcs = (void *)&vbox_mode_funcs;
+ dev->mode_config.min_width = 64;
+ dev->mode_config.min_height = 64;
+ dev->mode_config.preferred_depth = 24;
+ dev->mode_config.max_width = VBE_DISPI_MAX_XRES;
+ dev->mode_config.max_height = VBE_DISPI_MAX_YRES;
+
+ ret = vbox_mode_init(dev);
+ if (ret)
+ goto err_drm_mode_cleanup;
+
+ ret = vbox_irq_init(vbox);
+ if (ret)
+ goto err_mode_fini;
+
+ ret = vbox_fbdev_init(dev);
+ if (ret)
+ goto err_irq_fini;
+
+ return 0;
+
+err_irq_fini:
+ vbox_irq_fini(vbox);
+err_mode_fini:
+ vbox_mode_fini(dev);
+err_drm_mode_cleanup:
+ drm_mode_config_cleanup(dev);
+ vbox_mm_fini(vbox);
+err_hw_fini:
+ vbox_hw_fini(vbox);
+ return ret;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) || defined(RHEL_75)
+void vbox_driver_unload(struct drm_device *dev)
+#else
+int vbox_driver_unload(struct drm_device *dev)
+#endif
+{
+ struct vbox_private *vbox = dev->dev_private;
+
+ vbox_fbdev_fini(dev);
+ vbox_irq_fini(vbox);
+ vbox_mode_fini(dev);
+ drm_mode_config_cleanup(dev);
+ vbox_mm_fini(vbox);
+ vbox_hw_fini(vbox);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) && !defined(RHEL_75)
+ return 0;
+#endif
+}
+
+/**
+ * @note this is described in the DRM framework documentation. AST does not
+ * have it, but we get an oops on driver unload if it is not present.
+ */
+void vbox_driver_lastclose(struct drm_device *dev)
+{
+ struct vbox_private *vbox = dev->dev_private;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) || defined(RHEL_71)
+ if (vbox->fbdev)
+ drm_fb_helper_restore_fbdev_mode_unlocked(&vbox->fbdev->helper);
+#else
+ drm_modeset_lock_all(dev);
+ if (vbox->fbdev)
+ drm_fb_helper_restore_fbdev_mode(&vbox->fbdev->helper);
+ drm_modeset_unlock_all(dev);
+#endif
+}
+
+int vbox_gem_create(struct drm_device *dev,
+ u32 size, bool iskernel, struct drm_gem_object **obj)
+{
+ struct vbox_bo *vboxbo;
+ int ret;
+
+ *obj = NULL;
+
+ size = roundup(size, PAGE_SIZE);
+ if (size == 0)
+ return -EINVAL;
+
+ ret = vbox_bo_create(dev, size, 0, 0, &vboxbo);
+ if (ret) {
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("failed to allocate GEM object\n");
+ return ret;
+ }
+
+ *obj = &vboxbo->gem;
+
+ return 0;
+}
+
+int vbox_dumb_create(struct drm_file *file,
+ struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+ int ret;
+ struct drm_gem_object *gobj;
+ u32 handle;
+
+ args->pitch = args->width * ((args->bpp + 7) / 8);
+ args->size = args->pitch * args->height;
+
+ ret = vbox_gem_create(dev, args->size, false, &gobj);
+ if (ret)
+ return ret;
+
+ ret = drm_gem_handle_create(file, gobj, &handle);
+ drm_gem_object_put_unlocked(gobj);
+ if (ret)
+ return ret;
+
+ args->handle = handle;
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) && !defined(RHEL_73)
+int vbox_dumb_destroy(struct drm_file *file,
+ struct drm_device *dev, u32 handle)
+{
+ return drm_gem_handle_delete(file, handle);
+}
+#endif
+
+static void vbox_bo_unref(struct vbox_bo **bo)
+{
+ struct ttm_buffer_object *tbo;
+
+ if ((*bo) == NULL)
+ return;
+
+ tbo = &((*bo)->bo);
+ ttm_bo_unref(&tbo);
+ if (!tbo)
+ *bo = NULL;
+}
+
+void vbox_gem_free_object(struct drm_gem_object *obj)
+{
+ struct vbox_bo *vbox_bo = gem_to_vbox_bo(obj);
+
+ vbox_bo_unref(&vbox_bo);
+}
+
+static inline u64 vbox_bo_mmap_offset(struct vbox_bo *bo)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) && !defined(RHEL_70)
+ return bo->bo.addr_space_offset;
+#else
+ return drm_vma_node_offset_addr(&bo->bo.vma_node);
+#endif
+}
+
+int
+vbox_dumb_mmap_offset(struct drm_file *file,
+ struct drm_device *dev,
+ u32 handle, u64 *offset)
+{
+ struct drm_gem_object *obj;
+ int ret;
+ struct vbox_bo *bo;
+
+ mutex_lock(&dev->struct_mutex);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) || defined(RHEL_74)
+ obj = drm_gem_object_lookup(file, handle);
+#else
+ obj = drm_gem_object_lookup(dev, file, handle);
+#endif
+ if (!obj) {
+ ret = -ENOENT;
+ goto out_unlock;
+ }
+
+ bo = gem_to_vbox_bo(obj);
+ *offset = vbox_bo_mmap_offset(bo);
+
+ drm_gem_object_unreference(obj);
+ ret = 0;
+
+out_unlock:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
diff --git a/src/VBox/Additions/linux/drm/vbox_mode.c b/src/VBox/Additions/linux/drm/vbox_mode.c
new file mode 100644
index 00000000..f8cc0bff
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_mode.c
@@ -0,0 +1,857 @@
+/* $Id: vbox_mode.c $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ * This file is based on ast_mode.c
+ * Copyright 2012 Red Hat Inc.
+ * Parts based on xf86-video-ast
+ * Copyright (c) 2005 ASPEED Technology Inc.
+ *
+ * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ * Michael Thayer <michael.thayer@oracle.com,
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+#include "vbox_drv.h"
+#include <linux/export.h>
+#include <drm/drm_crtc_helper.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) || defined(RHEL_72)
+#include <drm/drm_plane_helper.h>
+#endif
+
+#include "VBoxVideo.h"
+
+static int vbox_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv,
+ u32 handle, u32 width, u32 height,
+ s32 hot_x, s32 hot_y);
+static int vbox_cursor_move(struct drm_crtc *crtc, int x, int y);
+
+/**
+ * Set a graphics mode. Poke any required values into registers, do an HGSMI
+ * mode set and tell the host we support advanced graphics functions.
+ */
+static void vbox_do_modeset(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
+{
+ struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
+ struct vbox_private *vbox;
+ int width, height, bpp, pitch;
+ u16 flags;
+ s32 x_offset, y_offset;
+
+ vbox = crtc->dev->dev_private;
+ width = mode->hdisplay ? mode->hdisplay : 640;
+ height = mode->vdisplay ? mode->vdisplay : 480;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) || defined(RHEL_75)
+ bpp = crtc->enabled ? CRTC_FB(crtc)->format->cpp[0] * 8 : 32;
+ pitch = crtc->enabled ? CRTC_FB(crtc)->pitches[0] : width * bpp / 8;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
+ bpp = crtc->enabled ? CRTC_FB(crtc)->bits_per_pixel : 32;
+ pitch = crtc->enabled ? CRTC_FB(crtc)->pitches[0] : width * bpp / 8;
+#else
+ bpp = crtc->enabled ? CRTC_FB(crtc)->bits_per_pixel : 32;
+ pitch = crtc->enabled ? CRTC_FB(crtc)->pitch : width * bpp / 8;
+#endif
+ x_offset = vbox->single_framebuffer ? crtc->x : vbox_crtc->x_hint;
+ y_offset = vbox->single_framebuffer ? crtc->y : vbox_crtc->y_hint;
+
+ /*
+ * This is the old way of setting graphics modes. It assumed one screen
+ * and a frame-buffer at the start of video RAM. On older versions of
+ * VirtualBox, certain parts of the code still assume that the first
+ * screen is programmed this way, so try to fake it.
+ */
+ if (vbox_crtc->crtc_id == 0 && crtc->enabled &&
+ vbox_crtc->fb_offset / pitch < 0xffff - crtc->y &&
+ vbox_crtc->fb_offset % (bpp / 8) == 0)
+ VBoxVideoSetModeRegisters(
+ width, height, pitch * 8 / bpp,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) || defined(RHEL_75)
+ CRTC_FB(crtc)->format->cpp[0] * 8,
+#else
+ CRTC_FB(crtc)->bits_per_pixel,
+#endif
+ 0,
+ vbox_crtc->fb_offset % pitch / bpp * 8 + crtc->x,
+ vbox_crtc->fb_offset / pitch + crtc->y);
+
+ flags = VBVA_SCREEN_F_ACTIVE;
+ flags |= (crtc->enabled && !vbox_crtc->blanked) ?
+ 0 : VBVA_SCREEN_F_BLANK;
+ flags |= vbox_crtc->disconnected ? VBVA_SCREEN_F_DISABLED : 0;
+ VBoxHGSMIProcessDisplayInfo(vbox->guest_pool, vbox_crtc->crtc_id,
+ x_offset, y_offset, vbox_crtc->fb_offset +
+ crtc->x * bpp / 8 + crtc->y * pitch,
+ pitch, width, height,
+ vbox_crtc->blanked ? 0 : bpp, flags);
+}
+
+static void vbox_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
+ struct vbox_private *vbox = crtc->dev->dev_private;
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ vbox_crtc->blanked = false;
+ /* Restart the refresh timer if necessary. */
+ schedule_delayed_work(&vbox->refresh_work, VBOX_REFRESH_PERIOD);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ vbox_crtc->blanked = true;
+ break;
+ }
+
+ mutex_lock(&vbox->hw_mutex);
+ vbox_do_modeset(crtc, &crtc->hwmode);
+ mutex_unlock(&vbox->hw_mutex);
+}
+
+static bool vbox_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+/*
+ * Try to map the layout of virtual screens to the range of the input device.
+ * Return true if we need to re-set the crtc modes due to screen offset
+ * changes.
+ */
+static bool vbox_set_up_input_mapping(struct vbox_private *vbox)
+{
+ struct drm_crtc *crtci;
+ struct drm_connector *connectori;
+ struct drm_framebuffer *fb1 = NULL;
+ bool single_framebuffer = true;
+ bool old_single_framebuffer = vbox->single_framebuffer;
+ u16 width = 0, height = 0;
+
+ /*
+ * Are we using an X.Org-style single large frame-buffer for all crtcs?
+ * If so then screen layout can be deduced from the crtc offsets.
+ * Same fall-back if this is the fbdev frame-buffer.
+ */
+ list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list, head) {
+ if (!fb1) {
+ fb1 = CRTC_FB(crtci);
+ if (to_vbox_framebuffer(fb1) == &vbox->fbdev->afb)
+ break;
+ } else if (CRTC_FB(crtci) && fb1 != CRTC_FB(crtci)) {
+ single_framebuffer = false;
+ }
+ }
+ if (single_framebuffer) {
+ list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list,
+ head) {
+ if (to_vbox_crtc(crtci)->crtc_id != 0)
+ continue;
+
+ vbox->single_framebuffer = true;
+ vbox->input_mapping_width = CRTC_FB(crtci)->width;
+ vbox->input_mapping_height = CRTC_FB(crtci)->height;
+ return old_single_framebuffer !=
+ vbox->single_framebuffer;
+ }
+ }
+ /* Otherwise calculate the total span of all screens. */
+ list_for_each_entry(connectori, &vbox->dev->mode_config.connector_list,
+ head) {
+ struct vbox_connector *vbox_connector =
+ to_vbox_connector(connectori);
+ struct vbox_crtc *vbox_crtc = vbox_connector->vbox_crtc;
+
+ width = max_t(u16, width, vbox_crtc->x_hint +
+ vbox_connector->mode_hint.width);
+ height = max_t(u16, height, vbox_crtc->y_hint +
+ vbox_connector->mode_hint.height);
+ }
+
+ vbox->single_framebuffer = false;
+ vbox->input_mapping_width = width;
+ vbox->input_mapping_height = height;
+
+ return old_single_framebuffer != vbox->single_framebuffer;
+}
+
+static int vbox_crtc_set_base(struct drm_crtc *crtc,
+ struct drm_framebuffer *old_fb, int x, int y)
+{
+ struct vbox_private *vbox = crtc->dev->dev_private;
+ struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
+ struct drm_gem_object *obj;
+ struct vbox_framebuffer *vbox_fb;
+ struct vbox_bo *bo;
+ int ret;
+ u64 gpu_addr;
+
+ vbox_fb = to_vbox_framebuffer(CRTC_FB(crtc));
+ obj = vbox_fb->obj;
+ bo = gem_to_vbox_bo(obj);
+
+ ret = vbox_bo_reserve(bo, false);
+ if (ret)
+ return ret;
+
+ ret = vbox_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr);
+ vbox_bo_unreserve(bo);
+ if (ret)
+ return ret;
+
+ /* Unpin the previous fb. Do this after the new one has been pinned rather
+ * than before and re-pinning it on failure in case that fails too. */
+ if (old_fb) {
+ vbox_fb = to_vbox_framebuffer(old_fb);
+ obj = vbox_fb->obj;
+ bo = gem_to_vbox_bo(obj);
+ ret = vbox_bo_reserve(bo, false);
+ /* This should never fail, as no one else should be accessing it and we
+ * should be running under the modeset locks. */
+ if (!ret) {
+ vbox_bo_unpin(bo);
+ vbox_bo_unreserve(bo);
+ }
+ }
+
+ if (&vbox->fbdev->afb == vbox_fb)
+ vbox_fbdev_set_base(vbox, gpu_addr);
+
+ vbox_crtc->fb_offset = gpu_addr;
+ if (vbox_set_up_input_mapping(vbox)) {
+ struct drm_crtc *crtci;
+
+ list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list,
+ head) {
+ vbox_do_modeset(crtci, &crtci->mode);
+ }
+ }
+
+ return 0;
+}
+
+static int vbox_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y, struct drm_framebuffer *old_fb)
+{
+ struct vbox_private *vbox = crtc->dev->dev_private;
+ int ret = vbox_crtc_set_base(crtc, old_fb, x, y);
+ if (ret)
+ return ret;
+ mutex_lock(&vbox->hw_mutex);
+ vbox_do_modeset(crtc, mode);
+ VBoxHGSMIUpdateInputMapping(vbox->guest_pool, 0, 0,
+ vbox->input_mapping_width,
+ vbox->input_mapping_height);
+ mutex_unlock(&vbox->hw_mutex);
+
+ return ret;
+}
+
+static void vbox_crtc_disable(struct drm_crtc *crtc)
+{
+}
+
+static void vbox_crtc_prepare(struct drm_crtc *crtc)
+{
+}
+
+static void vbox_crtc_commit(struct drm_crtc *crtc)
+{
+}
+
+static const struct drm_crtc_helper_funcs vbox_crtc_helper_funcs = {
+ .dpms = vbox_crtc_dpms,
+ .mode_fixup = vbox_crtc_mode_fixup,
+ .mode_set = vbox_crtc_mode_set,
+ .disable = vbox_crtc_disable,
+ .prepare = vbox_crtc_prepare,
+ .commit = vbox_crtc_commit,
+};
+
+static void vbox_crtc_reset(struct drm_crtc *crtc)
+{
+}
+
+static void vbox_crtc_destroy(struct drm_crtc *crtc)
+{
+ drm_crtc_cleanup(crtc);
+ kfree(crtc);
+}
+
+static const struct drm_crtc_funcs vbox_crtc_funcs = {
+ .cursor_move = vbox_cursor_move,
+ .cursor_set2 = vbox_cursor_set2,
+ .reset = vbox_crtc_reset,
+ .set_config = drm_crtc_helper_set_config,
+ /* .gamma_set = vbox_crtc_gamma_set, */
+ .destroy = vbox_crtc_destroy,
+};
+
+static struct vbox_crtc *vbox_crtc_init(struct drm_device *dev, unsigned int i)
+{
+ struct vbox_crtc *vbox_crtc;
+
+ vbox_crtc = kzalloc(sizeof(*vbox_crtc), GFP_KERNEL);
+ if (!vbox_crtc)
+ return NULL;
+
+ vbox_crtc->crtc_id = i;
+
+ drm_crtc_init(dev, &vbox_crtc->base, &vbox_crtc_funcs);
+ drm_mode_crtc_set_gamma_size(&vbox_crtc->base, 256);
+ drm_crtc_helper_add(&vbox_crtc->base, &vbox_crtc_helper_funcs);
+
+ return vbox_crtc;
+}
+
+static void vbox_encoder_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+ kfree(encoder);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) && !defined(RHEL_71)
+static struct drm_encoder *drm_encoder_find(struct drm_device *dev, u32 id)
+{
+ struct drm_mode_object *mo;
+
+ mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
+ return mo ? obj_to_encoder(mo) : NULL;
+}
+#endif
+
+static struct drm_encoder *vbox_best_single_encoder(struct drm_connector
+ *connector)
+{
+ int enc_id = connector->encoder_ids[0];
+
+ /* pick the encoder ids */
+ if (enc_id)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) || \
+ (defined(CONFIG_SUSE_VERSION) && \
+ LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) || \
+ defined(RHEL_76)
+ return drm_encoder_find(connector->dev, NULL, enc_id);
+#else
+ return drm_encoder_find(connector->dev, enc_id);
+#endif
+
+ return NULL;
+}
+
+static const struct drm_encoder_funcs vbox_enc_funcs = {
+ .destroy = vbox_encoder_destroy,
+};
+
+static void vbox_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool vbox_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void vbox_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void vbox_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void vbox_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static const struct drm_encoder_helper_funcs vbox_enc_helper_funcs = {
+ .dpms = vbox_encoder_dpms,
+ .mode_fixup = vbox_mode_fixup,
+ .prepare = vbox_encoder_prepare,
+ .commit = vbox_encoder_commit,
+ .mode_set = vbox_encoder_mode_set,
+};
+
+static struct drm_encoder *vbox_encoder_init(struct drm_device *dev,
+ unsigned int i)
+{
+ struct vbox_encoder *vbox_encoder;
+
+ vbox_encoder = kzalloc(sizeof(*vbox_encoder), GFP_KERNEL);
+ if (!vbox_encoder)
+ return NULL;
+
+ drm_encoder_init(dev, &vbox_encoder->base, &vbox_enc_funcs,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) || defined(RHEL_73)
+ DRM_MODE_ENCODER_DAC, NULL);
+#else
+ DRM_MODE_ENCODER_DAC);
+#endif
+ drm_encoder_helper_add(&vbox_encoder->base, &vbox_enc_helper_funcs);
+
+ vbox_encoder->base.possible_crtcs = 1 << i;
+ return &vbox_encoder->base;
+}
+
+/**
+ * Generate EDID data with a mode-unique serial number for the virtual
+ * monitor to try to persuade Unity that different modes correspond to
+ * different monitors and it should not try to force the same resolution on
+ * them.
+ */
+static void vbox_set_edid(struct drm_connector *connector, int width,
+ int height)
+{
+ enum { EDID_SIZE = 128 };
+ unsigned char edid[EDID_SIZE] = {
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */
+ 0x58, 0x58, /* manufacturer (VBX) */
+ 0x00, 0x00, /* product code */
+ 0x00, 0x00, 0x00, 0x00, /* serial number goes here */
+ 0x01, /* week of manufacture */
+ 0x00, /* year of manufacture */
+ 0x01, 0x03, /* EDID version */
+ 0x80, /* capabilities - digital */
+ 0x00, /* horiz. res in cm, zero for projectors */
+ 0x00, /* vert. res in cm */
+ 0x78, /* display gamma (120 == 2.2). */
+ 0xEE, /* features (standby, suspend, off, RGB, std */
+ /* colour space, preferred timing mode) */
+ 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54,
+ /* chromaticity for standard colour space. */
+ 0x00, 0x00, 0x00, /* no default timings */
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, /* no standard timings */
+ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x02, 0x02,
+ 0x02, 0x02,
+ /* descriptor block 1 goes below */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* descriptor block 2, monitor ranges */
+ 0x00, 0x00, 0x00, 0xFD, 0x00,
+ 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20,
+ 0x20, 0x20,
+ /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */
+ 0x20,
+ /* descriptor block 3, monitor name */
+ 0x00, 0x00, 0x00, 0xFC, 0x00,
+ 'V', 'B', 'O', 'X', ' ', 'm', 'o', 'n', 'i', 't', 'o', 'r',
+ '\n',
+ /* descriptor block 4: dummy data */
+ 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20,
+ 0x00, /* number of extensions */
+ 0x00 /* checksum goes here */
+ };
+ int clock = (width + 6) * (height + 6) * 60 / 10000;
+ unsigned int i, sum = 0;
+
+ edid[12] = width & 0xff;
+ edid[13] = width >> 8;
+ edid[14] = height & 0xff;
+ edid[15] = height >> 8;
+ edid[54] = clock & 0xff;
+ edid[55] = clock >> 8;
+ edid[56] = width & 0xff;
+ edid[58] = (width >> 4) & 0xf0;
+ edid[59] = height & 0xff;
+ edid[61] = (height >> 4) & 0xf0;
+ for (i = 0; i < EDID_SIZE - 1; ++i)
+ sum += edid[i];
+ edid[EDID_SIZE - 1] = (0x100 - (sum & 0xFF)) & 0xFF;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+ drm_connector_update_edid_property(connector, (struct edid *)edid);
+#else
+ drm_mode_connector_update_edid_property(connector, (struct edid *)edid);
+#endif
+}
+
+static int vbox_get_modes(struct drm_connector *connector)
+{
+ struct vbox_connector *vbox_connector = NULL;
+ struct drm_display_mode *mode = NULL;
+ struct vbox_private *vbox = NULL;
+ unsigned int num_modes = 0;
+ int preferred_width, preferred_height;
+
+ vbox_connector = to_vbox_connector(connector);
+ vbox = connector->dev->dev_private;
+ /*
+ * Heuristic: we do not want to tell the host that we support dynamic
+ * resizing unless we feel confident that the user space client using
+ * the video driver can handle hot-plug events. So the first time modes
+ * are queried after a "master" switch we tell the host that we do not,
+ * and immediately after we send the client a hot-plug notification as
+ * a test to see if they will respond and query again.
+ * That is also the reason why capabilities are reported to the host at
+ * this place in the code rather than elsewhere.
+ * We need to report the flags location before reporting the IRQ
+ * capability.
+ */
+ VBoxHGSMIReportFlagsLocation(vbox->guest_pool, GUEST_HEAP_OFFSET(vbox) +
+ HOST_FLAGS_OFFSET);
+ if (vbox_connector->vbox_crtc->crtc_id == 0)
+ vbox_report_caps(vbox);
+ if (!vbox->initial_mode_queried) {
+ if (vbox_connector->vbox_crtc->crtc_id == 0) {
+ vbox->initial_mode_queried = true;
+ vbox_report_hotplug(vbox);
+ }
+ return drm_add_modes_noedid(connector, 800, 600);
+ }
+ /* Also assume that a client which supports hot-plugging also knows
+ * how to update the screen in a way we can use, the only known
+ * relevent client which cannot is Plymouth in Ubuntu 14.04. */
+ vbox->need_refresh_timer = false;
+ num_modes = drm_add_modes_noedid(connector, 2560, 1600);
+ preferred_width = vbox_connector->mode_hint.width ?
+ vbox_connector->mode_hint.width : 1024;
+ preferred_height = vbox_connector->mode_hint.height ?
+ vbox_connector->mode_hint.height : 768;
+ mode = drm_cvt_mode(connector->dev, preferred_width, preferred_height,
+ 60, false, false, false);
+ if (mode) {
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, mode);
+ ++num_modes;
+ }
+ vbox_set_edid(connector, preferred_width, preferred_height);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) || defined(RHEL_72)
+ if (vbox_connector->vbox_crtc->x_hint != -1)
+ drm_object_property_set_value(&connector->base,
+ vbox->dev->mode_config.suggested_x_property,
+ vbox_connector->vbox_crtc->x_hint);
+ else
+ drm_object_property_set_value(&connector->base,
+ vbox->dev->mode_config.suggested_x_property, 0);
+
+ if (vbox_connector->vbox_crtc->y_hint != -1)
+ drm_object_property_set_value(&connector->base,
+ vbox->dev->mode_config.suggested_y_property,
+ vbox_connector->vbox_crtc->y_hint);
+ else
+ drm_object_property_set_value(&connector->base,
+ vbox->dev->mode_config.suggested_y_property, 0);
+#endif
+
+ return num_modes;
+}
+
+static int vbox_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static void vbox_connector_destroy(struct drm_connector *connector)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) && !defined(RHEL_72)
+ drm_sysfs_connector_remove(connector);
+#else
+ drm_connector_unregister(connector);
+#endif
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+static enum drm_connector_status
+vbox_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct vbox_connector *vbox_connector;
+
+ vbox_connector = to_vbox_connector(connector);
+
+ return vbox_connector->mode_hint.disconnected ?
+ connector_status_disconnected : connector_status_connected;
+}
+
+static int vbox_fill_modes(struct drm_connector *connector, u32 max_x,
+ u32 max_y)
+{
+ struct vbox_connector *vbox_connector;
+ struct drm_device *dev;
+ struct drm_display_mode *mode, *iterator;
+
+ vbox_connector = to_vbox_connector(connector);
+ dev = vbox_connector->base.dev;
+ list_for_each_entry_safe(mode, iterator, &connector->modes, head) {
+ list_del(&mode->head);
+ drm_mode_destroy(dev, mode);
+ }
+
+ return drm_helper_probe_single_connector_modes(connector, max_x, max_y);
+}
+
+static const struct drm_connector_helper_funcs vbox_connector_helper_funcs = {
+ .mode_valid = vbox_mode_valid,
+ .get_modes = vbox_get_modes,
+ .best_encoder = vbox_best_single_encoder,
+};
+
+static const struct drm_connector_funcs vbox_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = vbox_connector_detect,
+ .fill_modes = vbox_fill_modes,
+ .destroy = vbox_connector_destroy,
+};
+
+static int vbox_connector_init(struct drm_device *dev,
+ struct vbox_crtc *vbox_crtc,
+ struct drm_encoder *encoder)
+{
+ struct vbox_connector *vbox_connector;
+ struct drm_connector *connector;
+
+ vbox_connector = kzalloc(sizeof(*vbox_connector), GFP_KERNEL);
+ if (!vbox_connector)
+ return -ENOMEM;
+
+ connector = &vbox_connector->base;
+ vbox_connector->vbox_crtc = vbox_crtc;
+
+ drm_connector_init(dev, connector, &vbox_connector_funcs,
+ DRM_MODE_CONNECTOR_VGA);
+ drm_connector_helper_add(connector, &vbox_connector_helper_funcs);
+
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) || defined(RHEL_72)
+ drm_mode_create_suggested_offset_properties(dev);
+ drm_object_attach_property(&connector->base,
+ dev->mode_config.suggested_x_property, 0);
+ drm_object_attach_property(&connector->base,
+ dev->mode_config.suggested_y_property, 0);
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) && !defined(RHEL_72)
+ drm_sysfs_connector_add(connector);
+#else
+ drm_connector_register(connector);
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+ drm_connector_attach_encoder(connector, encoder);
+#else
+ drm_mode_connector_attach_encoder(connector, encoder);
+#endif
+
+ return 0;
+}
+
+int vbox_mode_init(struct drm_device *dev)
+{
+ struct vbox_private *vbox = dev->dev_private;
+ struct drm_encoder *encoder;
+ struct vbox_crtc *vbox_crtc;
+ unsigned int i;
+ int ret;
+
+ /* vbox_cursor_init(dev); */
+ for (i = 0; i < vbox->num_crtcs; ++i) {
+ vbox_crtc = vbox_crtc_init(dev, i);
+ if (!vbox_crtc)
+ return -ENOMEM;
+ encoder = vbox_encoder_init(dev, i);
+ if (!encoder)
+ return -ENOMEM;
+ ret = vbox_connector_init(dev, vbox_crtc, encoder);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void vbox_mode_fini(struct drm_device *dev)
+{
+ /* vbox_cursor_fini(dev); */
+}
+
+/**
+ * Copy the ARGB image and generate the mask, which is needed in case the host
+ * does not support ARGB cursors. The mask is a 1BPP bitmap with the bit set
+ * if the corresponding alpha value in the ARGB image is greater than 0xF0.
+ */
+static void copy_cursor_image(u8 *src, u8 *dst, u32 width, u32 height,
+ size_t mask_size)
+{
+ size_t line_size = (width + 7) / 8;
+ u32 i, j;
+
+ memcpy(dst + mask_size, src, width * height * 4);
+ for (i = 0; i < height; ++i)
+ for (j = 0; j < width; ++j)
+ if (((u32 *)src)[i * width + j] > 0xf0000000)
+ dst[i * line_size + j / 8] |= (0x80 >> (j % 8));
+}
+
+static int vbox_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv,
+ u32 handle, u32 width, u32 height,
+ s32 hot_x, s32 hot_y)
+{
+ struct vbox_private *vbox = crtc->dev->dev_private;
+ struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
+ struct ttm_bo_kmap_obj uobj_map;
+ size_t data_size, mask_size;
+ struct drm_gem_object *obj;
+ u32 flags, caps = 0;
+ struct vbox_bo *bo;
+ bool src_isiomem;
+ u8 *dst = NULL;
+ u8 *src;
+ int ret;
+
+ if (!handle) {
+ bool cursor_enabled = false;
+ struct drm_crtc *crtci;
+
+ /* Hide cursor. */
+ vbox_crtc->cursor_enabled = false;
+ list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list,
+ head) {
+ if (to_vbox_crtc(crtci)->cursor_enabled)
+ cursor_enabled = true;
+ }
+
+ if (!cursor_enabled)
+ VBoxHGSMIUpdatePointerShape(vbox->guest_pool, 0, 0, 0,
+ 0, 0, NULL, 0);
+ return 0;
+ }
+
+ vbox_crtc->cursor_enabled = true;
+
+ if (width > VBOX_MAX_CURSOR_WIDTH || height > VBOX_MAX_CURSOR_HEIGHT ||
+ width == 0 || height == 0)
+ return -EINVAL;
+ ret = VBoxQueryConfHGSMI(vbox->guest_pool,
+ VBOX_VBVA_CONF32_CURSOR_CAPABILITIES, &caps);
+ if (ret)
+ return ret == VERR_NO_MEMORY ? -ENOMEM : -EINVAL;
+
+ if (!(caps & VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE)) {
+ /*
+ * -EINVAL means cursor_set2() not supported, -EAGAIN means
+ * retry at once.
+ */
+ return -EBUSY;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) || defined(RHEL_74)
+ obj = drm_gem_object_lookup(file_priv, handle);
+#else
+ obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
+#endif
+ if (!obj) {
+ DRM_ERROR("Cannot find cursor object %x for crtc\n", handle);
+ return -ENOENT;
+ }
+
+ bo = gem_to_vbox_bo(obj);
+ ret = vbox_bo_reserve(bo, false);
+ if (ret)
+ goto out_unref_obj;
+
+ /*
+ * The mask must be calculated based on the alpha
+ * channel, one bit per ARGB word, and must be 32-bit
+ * padded.
+ */
+ mask_size = ((width + 7) / 8 * height + 3) & ~3;
+ data_size = width * height * 4 + mask_size;
+ vbox->cursor_hot_x = hot_x;
+ vbox->cursor_hot_y = hot_y;
+ vbox->cursor_width = width;
+ vbox->cursor_height = height;
+ vbox->cursor_data_size = data_size;
+ dst = vbox->cursor_data;
+
+ ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &uobj_map);
+ if (ret) {
+ vbox->cursor_data_size = 0;
+ goto out_unreserve_bo;
+ }
+
+ src = ttm_kmap_obj_virtual(&uobj_map, &src_isiomem);
+ if (src_isiomem) {
+ DRM_ERROR("src cursor bo not in main memory\n");
+ ret = -EIO;
+ goto out_unmap_bo;
+ }
+
+ copy_cursor_image(src, dst, width, height, mask_size);
+
+ flags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE |
+ VBOX_MOUSE_POINTER_ALPHA;
+ ret = VBoxHGSMIUpdatePointerShape(vbox->guest_pool, flags,
+ vbox->cursor_hot_x, vbox->cursor_hot_y,
+ width, height, dst, data_size);
+ ret = ret == VINF_SUCCESS ? 0 : ret == VERR_NO_MEMORY ? -ENOMEM :
+ ret == VERR_NOT_SUPPORTED ? -EBUSY : -EINVAL;
+
+out_unmap_bo:
+ ttm_bo_kunmap(&uobj_map);
+out_unreserve_bo:
+ vbox_bo_unreserve(bo);
+out_unref_obj:
+ drm_gem_object_put_unlocked(obj);
+
+ return ret;
+}
+
+static int vbox_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+ struct vbox_private *vbox = crtc->dev->dev_private;
+ s32 crtc_x =
+ vbox->single_framebuffer ? crtc->x : to_vbox_crtc(crtc)->x_hint;
+ s32 crtc_y =
+ vbox->single_framebuffer ? crtc->y : to_vbox_crtc(crtc)->y_hint;
+ int ret;
+
+ x += vbox->cursor_hot_x;
+ y += vbox->cursor_hot_y;
+ if (x + crtc_x < 0 || y + crtc_y < 0 ||
+ x + crtc_x >= vbox->input_mapping_width ||
+ y + crtc_y >= vbox->input_mapping_width ||
+ vbox->cursor_data_size == 0)
+ return 0;
+ ret = VBoxHGSMICursorPosition(vbox->guest_pool, true, x + crtc_x,
+ y + crtc_y, NULL, NULL);
+ return ret == VINF_SUCCESS ? 0 : ret == VERR_NO_MEMORY ? -ENOMEM : ret ==
+ VERR_NOT_SUPPORTED ? -EBUSY : -EINVAL;
+}
diff --git a/src/VBox/Additions/linux/drm/vbox_prime.c b/src/VBox/Additions/linux/drm/vbox_prime.c
new file mode 100644
index 00000000..f46335c4
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_prime.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017-2019 Oracle Corporation
+ * This file is based on qxl_prime.c
+ * Copyright 2017 Canonical
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Andreas Pokorny
+ */
+
+#include "vbox_drv.h"
+
+/*
+ * Based on qxl_prime.c:
+ * Empty Implementations as there should not be any other driver for a virtual
+ * device that might share buffers with vboxvideo
+ */
+
+int vbox_gem_prime_pin(struct drm_gem_object *obj)
+{
+ WARN_ONCE(1, "not implemented");
+ return -ENOSYS;
+}
+
+void vbox_gem_prime_unpin(struct drm_gem_object *obj)
+{
+ WARN_ONCE(1, "not implemented");
+}
+
+struct sg_table *vbox_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+ WARN_ONCE(1, "not implemented");
+ return ERR_PTR(-ENOSYS);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) && !defined(RHEL_72)
+struct drm_gem_object *vbox_gem_prime_import_sg_table(
+ struct drm_device *dev, size_t size, struct sg_table *table)
+#else
+struct drm_gem_object *vbox_gem_prime_import_sg_table(
+ struct drm_device *dev, struct dma_buf_attachment *attach,
+ struct sg_table *table)
+#endif
+{
+ WARN_ONCE(1, "not implemented");
+ return ERR_PTR(-ENOSYS);
+}
+
+void *vbox_gem_prime_vmap(struct drm_gem_object *obj)
+{
+ WARN_ONCE(1, "not implemented");
+ return ERR_PTR(-ENOSYS);
+}
+
+void vbox_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+ WARN_ONCE(1, "not implemented");
+}
+
+int vbox_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *area)
+{
+ WARN_ONCE(1, "not implemented");
+ return -ENOSYS;
+}
diff --git a/src/VBox/Additions/linux/drm/vbox_ttm.c b/src/VBox/Additions/linux/drm/vbox_ttm.c
new file mode 100644
index 00000000..ec615787
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_ttm.c
@@ -0,0 +1,550 @@
+/* $Id: vbox_ttm.c $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ * This file is based on ast_ttm.c
+ * Copyright 2012 Red Hat Inc.
+ *
+ * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ *
+ * Authors: Dave Airlie <airlied@redhat.com>
+ * Michael Thayer <michael.thayer@oracle.com>
+ */
+#include "vbox_drv.h"
+#include <ttm/ttm_page_alloc.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) && !defined(RHEL_72)
+#define PLACEMENT_FLAGS(placement) (placement)
+#else
+#define PLACEMENT_FLAGS(placement) ((placement).flags)
+#endif
+
+static inline struct vbox_private *vbox_bdev(struct ttm_bo_device *bd)
+{
+ return container_of(bd, struct vbox_private, ttm.bdev);
+}
+
+static int vbox_ttm_mem_global_init(struct drm_global_reference *ref)
+{
+ return ttm_mem_global_init(ref->object);
+}
+
+static void vbox_ttm_mem_global_release(struct drm_global_reference *ref)
+{
+ ttm_mem_global_release(ref->object);
+}
+
+/**
+ * Adds the vbox memory manager object/structures to the global memory manager.
+ */
+static int vbox_ttm_global_init(struct vbox_private *vbox)
+{
+ struct drm_global_reference *global_ref;
+ int ret;
+
+ global_ref = &vbox->ttm.mem_global_ref;
+ global_ref->global_type = DRM_GLOBAL_TTM_MEM;
+ global_ref->size = sizeof(struct ttm_mem_global);
+ global_ref->init = &vbox_ttm_mem_global_init;
+ global_ref->release = &vbox_ttm_mem_global_release;
+ ret = drm_global_item_ref(global_ref);
+ if (ret) {
+ DRM_ERROR("Failed setting up TTM memory subsystem.\n");
+ return ret;
+ }
+
+ vbox->ttm.bo_global_ref.mem_glob = vbox->ttm.mem_global_ref.object;
+ global_ref = &vbox->ttm.bo_global_ref.ref;
+ global_ref->global_type = DRM_GLOBAL_TTM_BO;
+ global_ref->size = sizeof(struct ttm_bo_global);
+ global_ref->init = &ttm_bo_global_init;
+ global_ref->release = &ttm_bo_global_release;
+
+ ret = drm_global_item_ref(global_ref);
+ if (ret) {
+ DRM_ERROR("Failed setting up TTM BO subsystem.\n");
+ drm_global_item_unref(&vbox->ttm.mem_global_ref);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * Removes the vbox memory manager object from the global memory manager.
+ */
+static void vbox_ttm_global_release(struct vbox_private *vbox)
+{
+ drm_global_item_unref(&vbox->ttm.bo_global_ref.ref);
+ drm_global_item_unref(&vbox->ttm.mem_global_ref);
+}
+
+static void vbox_bo_ttm_destroy(struct ttm_buffer_object *tbo)
+{
+ struct vbox_bo *bo;
+
+ bo = container_of(tbo, struct vbox_bo, bo);
+
+ drm_gem_object_release(&bo->gem);
+ kfree(bo);
+}
+
+static bool vbox_ttm_bo_is_vbox_bo(struct ttm_buffer_object *bo)
+{
+ if (bo->destroy == &vbox_bo_ttm_destroy)
+ return true;
+
+ return false;
+}
+
+static int
+vbox_bo_init_mem_type(struct ttm_bo_device *bdev, u32 type,
+ struct ttm_mem_type_manager *man)
+{
+ switch (type) {
+ case TTM_PL_SYSTEM:
+ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
+ man->available_caching = TTM_PL_MASK_CACHING;
+ man->default_caching = TTM_PL_FLAG_CACHED;
+ break;
+ case TTM_PL_VRAM:
+ man->func = &ttm_bo_manager_func;
+ man->flags = TTM_MEMTYPE_FLAG_FIXED | TTM_MEMTYPE_FLAG_MAPPABLE;
+ man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC;
+ man->default_caching = TTM_PL_FLAG_WC;
+ break;
+ default:
+ DRM_ERROR("Unsupported memory type %u\n", (unsigned int)type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void
+vbox_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
+{
+ struct vbox_bo *vboxbo = vbox_bo(bo);
+
+ if (!vbox_ttm_bo_is_vbox_bo(bo))
+ return;
+
+ vbox_ttm_placement(vboxbo, TTM_PL_FLAG_SYSTEM);
+ *pl = vboxbo->placement;
+}
+
+static int vbox_bo_verify_access(struct ttm_buffer_object *bo,
+ struct file *filp)
+{
+ return 0;
+}
+
+static int vbox_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
+ struct ttm_mem_reg *mem)
+{
+ struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
+ struct vbox_private *vbox = vbox_bdev(bdev);
+
+ mem->bus.addr = NULL;
+ mem->bus.offset = 0;
+ mem->bus.size = mem->num_pages << PAGE_SHIFT;
+ mem->bus.base = 0;
+ mem->bus.is_iomem = false;
+ if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
+ return -EINVAL;
+ switch (mem->mem_type) {
+ case TTM_PL_SYSTEM:
+ /* system memory */
+ return 0;
+ case TTM_PL_VRAM:
+ mem->bus.offset = mem->start << PAGE_SHIFT;
+ mem->bus.base = pci_resource_start(vbox->dev->pdev, 0);
+ mem->bus.is_iomem = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void vbox_ttm_io_mem_free(struct ttm_bo_device *bdev,
+ struct ttm_mem_reg *mem)
+{
+}
+
+static void vbox_ttm_backend_destroy(struct ttm_tt *tt)
+{
+ ttm_tt_fini(tt);
+ kfree(tt);
+}
+
+static struct ttm_backend_func vbox_tt_backend_func = {
+ .destroy = &vbox_ttm_backend_destroy,
+};
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)) && !defined(RHEL_76)
+static struct ttm_tt *vbox_ttm_tt_create(struct ttm_bo_device *bdev,
+ unsigned long size,
+ u32 page_flags,
+ struct page *dummy_read_page)
+#else
+static struct ttm_tt *vbox_ttm_tt_create(struct ttm_buffer_object *bo,
+ u32 page_flags)
+#endif
+{
+ struct ttm_tt *tt;
+
+ tt = kzalloc(sizeof(*tt), GFP_KERNEL);
+ if (!tt)
+ return NULL;
+
+ tt->func = &vbox_tt_backend_func;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)) && !defined(RHEL_76)
+ if (ttm_tt_init(tt, bdev, size, page_flags, dummy_read_page)) {
+#else
+ if (ttm_tt_init(tt, bo, page_flags)) {
+#endif
+ kfree(tt);
+ return NULL;
+ }
+
+ return tt;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
+# if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) && !defined(RHEL_76)
+static int vbox_ttm_tt_populate(struct ttm_tt *ttm)
+{
+ return ttm_pool_populate(ttm);
+}
+# else
+static int vbox_ttm_tt_populate(struct ttm_tt *ttm,
+ struct ttm_operation_ctx *ctx)
+{
+ return ttm_pool_populate(ttm, ctx);
+}
+# endif
+
+static void vbox_ttm_tt_unpopulate(struct ttm_tt *ttm)
+{
+ ttm_pool_unpopulate(ttm);
+}
+#endif
+
+static struct ttm_bo_driver vbox_bo_driver = {
+ .ttm_tt_create = vbox_ttm_tt_create,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
+ .ttm_tt_populate = vbox_ttm_tt_populate,
+ .ttm_tt_unpopulate = vbox_ttm_tt_unpopulate,
+#endif
+ .init_mem_type = vbox_bo_init_mem_type,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) || defined(RHEL_74)
+ .eviction_valuable = ttm_bo_eviction_valuable,
+#endif
+ .evict_flags = vbox_bo_evict_flags,
+ .verify_access = vbox_bo_verify_access,
+ .io_mem_reserve = &vbox_ttm_io_mem_reserve,
+ .io_mem_free = &vbox_ttm_io_mem_free,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) || defined(RHEL_75)
+# if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) && !defined(RHEL_76)
+ .io_mem_pfn = ttm_bo_default_io_mem_pfn,
+# endif
+#endif
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) \
+ || defined(RHEL_74)
+# ifndef RHEL_75
+ .lru_tail = &ttm_bo_default_lru_tail,
+ .swap_lru_tail = &ttm_bo_default_swap_lru_tail,
+# endif
+#endif
+};
+
+int vbox_mm_init(struct vbox_private *vbox)
+{
+ int ret;
+ struct drm_device *dev = vbox->dev;
+ struct ttm_bo_device *bdev = &vbox->ttm.bdev;
+
+ ret = vbox_ttm_global_init(vbox);
+ if (ret)
+ return ret;
+
+ ret = ttm_bo_device_init(&vbox->ttm.bdev,
+ vbox->ttm.bo_global_ref.ref.object,
+ &vbox_bo_driver,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) || defined(RHEL_71)
+ dev->anon_inode->i_mapping,
+#endif
+ DRM_FILE_PAGE_OFFSET, true);
+ if (ret) {
+ DRM_ERROR("Error initialising bo driver; %d\n", ret);
+ goto err_ttm_global_release;
+ }
+
+ ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM,
+ vbox->available_vram_size >> PAGE_SHIFT);
+ if (ret) {
+ DRM_ERROR("Failed ttm VRAM init: %d\n", ret);
+ goto err_device_release;
+ }
+
+#ifdef DRM_MTRR_WC
+ vbox->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0),
+ pci_resource_len(dev->pdev, 0),
+ DRM_MTRR_WC);
+#else
+ vbox->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0),
+ pci_resource_len(dev->pdev, 0));
+#endif
+ return 0;
+
+err_device_release:
+ ttm_bo_device_release(&vbox->ttm.bdev);
+err_ttm_global_release:
+ vbox_ttm_global_release(vbox);
+ return ret;
+}
+
+void vbox_mm_fini(struct vbox_private *vbox)
+{
+#ifdef DRM_MTRR_WC
+ drm_mtrr_del(vbox->fb_mtrr,
+ pci_resource_start(vbox->dev->pdev, 0),
+ pci_resource_len(vbox->dev->pdev, 0), DRM_MTRR_WC);
+#else
+ arch_phys_wc_del(vbox->fb_mtrr);
+#endif
+ ttm_bo_device_release(&vbox->ttm.bdev);
+ vbox_ttm_global_release(vbox);
+}
+
+void vbox_ttm_placement(struct vbox_bo *bo, int domain)
+{
+ u32 c = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) && !defined(RHEL_72)
+ bo->placement.fpfn = 0;
+ bo->placement.lpfn = 0;
+#else
+ unsigned int i;
+#endif
+
+ bo->placement.placement = bo->placements;
+ bo->placement.busy_placement = bo->placements;
+
+ if (domain & TTM_PL_FLAG_VRAM)
+ PLACEMENT_FLAGS(bo->placements[c++]) =
+ TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
+ if (domain & TTM_PL_FLAG_SYSTEM)
+ PLACEMENT_FLAGS(bo->placements[c++]) =
+ TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+ if (!c)
+ PLACEMENT_FLAGS(bo->placements[c++]) =
+ TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+
+ bo->placement.num_placement = c;
+ bo->placement.num_busy_placement = c;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) || defined(RHEL_72)
+ for (i = 0; i < c; ++i) {
+ bo->placements[i].fpfn = 0;
+ bo->placements[i].lpfn = 0;
+ }
+#endif
+}
+
+int vbox_bo_create(struct drm_device *dev, int size, int align,
+ u32 flags, struct vbox_bo **pvboxbo)
+{
+ struct vbox_private *vbox = dev->dev_private;
+ struct vbox_bo *vboxbo;
+ size_t acc_size;
+ int ret;
+
+ vboxbo = kzalloc(sizeof(*vboxbo), GFP_KERNEL);
+ if (!vboxbo)
+ return -ENOMEM;
+
+ ret = drm_gem_object_init(dev, &vboxbo->gem, size);
+ if (ret)
+ goto err_free_vboxbo;
+
+ vboxbo->bo.bdev = &vbox->ttm.bdev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) && !defined(RHEL_71)
+ vboxbo->bo.bdev->dev_mapping = dev->dev_mapping;
+#endif
+
+ vbox_ttm_placement(vboxbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
+
+ acc_size = ttm_bo_dma_acc_size(&vbox->ttm.bdev, size,
+ sizeof(struct vbox_bo));
+
+ ret = ttm_bo_init(&vbox->ttm.bdev, &vboxbo->bo, size,
+ ttm_bo_type_device, &vboxbo->placement,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) && !defined(RHEL_76)
+ align >> PAGE_SHIFT, false, NULL, acc_size,
+#else
+ align >> PAGE_SHIFT, false, acc_size,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) || defined(RHEL_72)
+ NULL, NULL, vbox_bo_ttm_destroy);
+#else
+ NULL, vbox_bo_ttm_destroy);
+#endif
+ if (ret)
+ goto err_free_vboxbo;
+
+ *pvboxbo = vboxbo;
+
+ return 0;
+
+err_free_vboxbo:
+ kfree(vboxbo);
+ return ret;
+}
+
+static inline u64 vbox_bo_gpu_offset(struct vbox_bo *bo)
+{
+ return bo->bo.offset;
+}
+
+int vbox_bo_pin(struct vbox_bo *bo, u32 pl_flag, u64 *gpu_addr)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)) || defined(RHEL_76)
+ struct ttm_operation_ctx ctx = { false, false };
+#endif
+ int i, ret;
+
+ if (bo->pin_count) {
+ bo->pin_count++;
+ if (gpu_addr)
+ *gpu_addr = vbox_bo_gpu_offset(bo);
+
+ return 0;
+ }
+
+ vbox_ttm_placement(bo, pl_flag);
+
+ for (i = 0; i < bo->placement.num_placement; i++)
+ PLACEMENT_FLAGS(bo->placements[i]) |= TTM_PL_FLAG_NO_EVICT;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) && !defined(RHEL_76)
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
+#else
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx);
+#endif
+ if (ret)
+ return ret;
+
+ bo->pin_count = 1;
+
+ if (gpu_addr)
+ *gpu_addr = vbox_bo_gpu_offset(bo);
+
+ return 0;
+}
+
+int vbox_bo_unpin(struct vbox_bo *bo)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)) || defined(RHEL_76)
+ struct ttm_operation_ctx ctx = { false, false };
+#endif
+ int i, ret;
+
+ if (!bo->pin_count) {
+ DRM_ERROR("unpin bad %p\n", bo);
+ return 0;
+ }
+ bo->pin_count--;
+ if (bo->pin_count)
+ return 0;
+
+ for (i = 0; i < bo->placement.num_placement; i++)
+ PLACEMENT_FLAGS(bo->placements[i]) &= ~TTM_PL_FLAG_NO_EVICT;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) && !defined(RHEL_76)
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
+#else
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx);
+#endif
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Move a vbox-owned buffer object to system memory if no one else has it
+ * pinned. The caller must have pinned it previously, and this call will
+ * release the caller's pin.
+ */
+int vbox_bo_push_sysram(struct vbox_bo *bo)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)) || defined(RHEL_76)
+ struct ttm_operation_ctx ctx = { false, false };
+#endif
+ int i, ret;
+
+ if (!bo->pin_count) {
+ DRM_ERROR("unpin bad %p\n", bo);
+ return 0;
+ }
+ bo->pin_count--;
+ if (bo->pin_count)
+ return 0;
+
+ if (bo->kmap.virtual)
+ ttm_bo_kunmap(&bo->kmap);
+
+ vbox_ttm_placement(bo, TTM_PL_FLAG_SYSTEM);
+
+ for (i = 0; i < bo->placement.num_placement; i++)
+ PLACEMENT_FLAGS(bo->placements[i]) |= TTM_PL_FLAG_NO_EVICT;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) && !defined(RHEL_76)
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
+#else
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx);
+#endif
+ if (ret) {
+ DRM_ERROR("pushing to VRAM failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int vbox_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_file *file_priv;
+ struct vbox_private *vbox;
+
+ if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
+ return -EINVAL;
+
+ file_priv = filp->private_data;
+ vbox = file_priv->minor->dev->dev_private;
+
+ return ttm_bo_mmap(filp, vma, &vbox->ttm.bdev);
+}
diff --git a/src/VBox/Additions/linux/export_modules.sh b/src/VBox/Additions/linux/export_modules.sh
new file mode 100755
index 00000000..85f756a2
--- /dev/null
+++ b/src/VBox/Additions/linux/export_modules.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+# $Id$
+## @file
+# Create a tar archive containing the sources of the Linux guest kernel modules.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+export LC_ALL=C
+
+# The below is GNU-specific. See VBox.sh for the longer Solaris/OS X version.
+TARGET=`readlink -e -- "${0}"` || exit 1
+MY_DIR="${TARGET%/[!/]*}"
+
+if [ -z "${1}" ] || { [ "x${1}" = x--folder ] && [ -z "${2}" ]; }; then
+ echo "Usage: $0 <filename.tar.gz>"
+ echo " Export VirtualBox kernel modules to <filename.tar.gz>."
+ echo "Usage: $0 --folder <folder>"
+ echo " Copy VirtualBox kernel module source to <folder>."
+ exit 1
+fi
+
+if test "x${1}" = x--folder; then
+ PATH_OUT="${2}"
+else
+ PATH_OUT="`cd \`dirname $1\`; pwd`/.vbox_modules"
+ FILE_OUT="`cd \`dirname $1\`; pwd`/`basename $1`"
+fi
+PATH_ROOT="`cd ${MY_DIR}/../../../..; pwd`"
+PATH_LOG=/tmp/vbox-export-guest.log
+PATH_LINUX="$PATH_ROOT/src/VBox/Additions/linux"
+PATH_VBOXGUEST="$PATH_ROOT/src/VBox/Additions/common/VBoxGuest"
+PATH_VBOXSF="$PATH_ROOT/src/VBox/Additions/linux/sharedfolders"
+PATH_VBOXVIDEO="$PATH_ROOT/src/VBox/Additions/linux/drm"
+
+VBOX_VERSION_MAJOR=`sed -e "s/^ *VBOX_VERSION_MAJOR *= \+\([0-9]\+\)/\1/;t;d" $PATH_ROOT/Version.kmk`
+VBOX_VERSION_MINOR=`sed -e "s/^ *VBOX_VERSION_MINOR *= \+\([0-9]\+\)/\1/;t;d" $PATH_ROOT/Version.kmk`
+VBOX_VERSION_BUILD=`sed -e "s/^ *VBOX_VERSION_BUILD *= \+\([0-9]\+\)/\1/;t;d" $PATH_ROOT/Version.kmk`
+VBOX_SVN_REV=`sed -e 's/^ *VBOX_SVN_REV_FALLBACK *:= \+\$(patsubst *%:,, *\$Rev: *\([0-9]\+\) *\$ *) */\1/;t;d' $PATH_ROOT/Config.kmk`
+VBOX_VENDOR=`sed -e 's/^ *VBOX_VENDOR *= \+\(.\+\)/\1/;t;d' $PATH_ROOT/Config.kmk`
+VBOX_VENDOR_SHORT=`sed -e 's/^ *VBOX_VENDOR_SHORT *= \+\(.\+\)/\1/;t;d' $PATH_ROOT/Config.kmk`
+VBOX_PRODUCT=`sed -e 's/^ *VBOX_PRODUCT *= \+\(.\+\)/\1/;t;d' $PATH_ROOT/Config.kmk`
+VBOX_C_YEAR=`date +%Y`
+
+. $PATH_VBOXGUEST/linux/files_vboxguest
+. $PATH_VBOXSF/files_vboxsf
+. $PATH_VBOXVIDEO/files_vboxvideo_drv
+
+# Temporary path for creating the modules, will be removed later
+mkdir -p $PATH_OUT || exit 1
+
+# Create auto-generated version file, needed by all modules
+echo "#ifndef ___version_generated_h___" > $PATH_OUT/version-generated.h
+echo "#define ___version_generated_h___" >> $PATH_OUT/version-generated.h
+echo "" >> $PATH_OUT/version-generated.h
+echo "#define VBOX_VERSION_MAJOR $VBOX_VERSION_MAJOR" >> $PATH_OUT/version-generated.h
+echo "#define VBOX_VERSION_MINOR $VBOX_VERSION_MINOR" >> $PATH_OUT/version-generated.h
+echo "#define VBOX_VERSION_BUILD $VBOX_VERSION_BUILD" >> $PATH_OUT/version-generated.h
+echo "#define VBOX_VERSION_STRING_RAW \"$VBOX_VERSION_MAJOR.$VBOX_VERSION_MINOR.$VBOX_VERSION_BUILD\"" >> $PATH_OUT/version-generated.h
+echo "#define VBOX_VERSION_STRING \"$VBOX_VERSION_MAJOR.$VBOX_VERSION_MINOR.$VBOX_VERSION_BUILD\"" >> $PATH_OUT/version-generated.h
+echo "#define VBOX_API_VERSION_STRING \"${VBOX_VERSION_MAJOR}_${VBOX_VERSION_MINOR}\"" >> $PATH_OUT/version-generated.h
+echo "#define VBOX_PRIVATE_BUILD_DESC \"Private build with export_modules\"" >> $PATH_OUT/version-generated.h
+echo "" >> $PATH_OUT/version-generated.h
+echo "#endif" >> $PATH_OUT/version-generated.h
+
+# Create auto-generated revision file, needed by all modules
+echo "#ifndef __revision_generated_h__" > $PATH_OUT/revision-generated.h
+echo "#define __revision_generated_h__" >> $PATH_OUT/revision-generated.h
+echo "" >> $PATH_OUT/revision-generated.h
+echo "#define VBOX_SVN_REV $VBOX_SVN_REV" >> $PATH_OUT/revision-generated.h
+echo "" >> $PATH_OUT/revision-generated.h
+echo "#endif" >> $PATH_OUT/revision-generated.h
+
+# Create auto-generated product file, needed by all modules
+echo "#ifndef ___product_generated_h___" > $PATH_OUT/product-generated.h
+echo "#define ___product_generated_h___" >> $PATH_OUT/product-generated.h
+echo "" >> $PATH_OUT/product-generated.h
+echo "#define VBOX_VENDOR \"$VBOX_VENDOR\"" >> $PATH_OUT/product-generated.h
+echo "#define VBOX_VENDOR_SHORT \"$VBOX_VENDOR_SHORT\"" >> $PATH_OUT/product-generated.h
+echo "" >> $PATH_OUT/product-generated.h
+echo "#define VBOX_PRODUCT \"$VBOX_PRODUCT\"" >> $PATH_OUT/product-generated.h
+echo "#define VBOX_C_YEAR \"$VBOX_C_YEAR\"" >> $PATH_OUT/product-generated.h
+echo "" >> $PATH_OUT/product-generated.h
+echo "#endif" >> $PATH_OUT/product-generated.h
+
+# vboxguest (VirtualBox guest kernel module)
+mkdir $PATH_OUT/vboxguest || exit 1
+for f in $FILES_VBOXGUEST_NOBIN; do
+ install -D -m 0644 `echo $f|cut -d'=' -f1` "$PATH_OUT/vboxguest/`echo $f|cut -d'>' -f2`"
+done
+for f in $FILES_VBOXGUEST_BIN; do
+ install -D -m 0755 `echo $f|cut -d'=' -f1` "$PATH_OUT/vboxguest/`echo $f|cut -d'>' -f2`"
+done
+
+# vboxsf (VirtualBox guest kernel module for shared folders)
+mkdir $PATH_OUT/vboxsf || exit 1
+for f in $FILES_VBOXSF_NOBIN; do
+ install -D -m 0644 `echo $f|cut -d'=' -f1` "$PATH_OUT/vboxsf/`echo $f|cut -d'>' -f2`"
+done
+for f in $FILES_VBOXSF_BIN; do
+ install -D -m 0755 `echo $f|cut -d'=' -f1` "$PATH_OUT/vboxsf/`echo $f|cut -d'>' -f2`"
+done
+
+# vboxvideo (VirtualBox guest kernel module for drm support)
+mkdir $PATH_OUT/vboxvideo || exit 1
+for f in $FILES_VBOXVIDEO_DRM_NOBIN; do
+ install -D -m 0644 `echo $f|cut -d'=' -f1` "$PATH_OUT/vboxvideo/`echo $f|cut -d'>' -f2`"
+done
+for f in $FILES_VBOXVIDEO_DRM_BIN; do
+ install -D -m 0755 `echo $f|cut -d'=' -f1` "$PATH_OUT/vboxvideo/`echo $f|cut -d'>' -f2`"
+done
+sed -f $PATH_VBOXVIDEO/indent.sed -i $PATH_OUT/vboxvideo/*.[ch]
+
+# convenience Makefile
+install -D -m 0644 $PATH_LINUX/Makefile "$PATH_OUT/Makefile"
+
+# Only temporary, omit from archive
+rm $PATH_OUT/version-generated.h
+rm $PATH_OUT/revision-generated.h
+rm $PATH_OUT/product-generated.h
+
+# If we are exporting to a folder then stop now.
+test "x${1}" = x--folder && exit 0
+
+# Do a test build
+echo Doing a test build, this may take a while.
+make -C $PATH_OUT > $PATH_LOG 2>&1 &&
+ make -C $PATH_OUT clean >> $PATH_LOG 2>&1 ||
+ echo "Warning: test build failed. Please check $PATH_LOG"
+
+# Create the archive
+tar -czf $FILE_OUT -C $PATH_OUT . || exit 1
+
+# Remove the temporary directory
+rm -r $PATH_OUT
+
diff --git a/src/VBox/Additions/linux/installer/.scm-settings b/src/VBox/Additions/linux/installer/.scm-settings
new file mode 100644
index 00000000..6eecc008
--- /dev/null
+++ b/src/VBox/Additions/linux/installer/.scm-settings
@@ -0,0 +1,20 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for linux guest additions installer.
+#
+
+#
+# Copyright (C) 2010-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+
+/install.sh.in: --treat-as .sh
+/deffiles: --treat-as files_
diff --git a/src/VBox/Additions/linux/installer/autorun.sh b/src/VBox/Additions/linux/installer/autorun.sh
new file mode 100755
index 00000000..5469daf6
--- /dev/null
+++ b/src/VBox/Additions/linux/installer/autorun.sh
@@ -0,0 +1,190 @@
+#!/bin/sh
+# $Id: autorun.sh $
+## @file
+# VirtualBox Guest Additions installation script for *nix guests
+#
+
+#
+# Copyright (C) 2009-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+PATH=$PATH:/bin:/sbin:/usr/sbin
+
+# Deal with differing "which" semantics
+mywhich() {
+ which "$1" 2>/dev/null | grep -v "no $1"
+}
+
+# Get the name and execute switch for a useful terminal emulator
+#
+# Sets $gxtpath to the emulator path or empty
+# Sets $gxttitle to the "title" switch for that emulator
+# Sets $gxtexec to the "execute" switch for that emulator
+# May clobber $gtx*
+# Calls mywhich
+getxterm() {
+ # gnome-terminal and mate-terminal use -e differently to other emulators
+ for gxti in "konsole --title -e" "gnome-terminal --title -x" "mate-terminal --title -x" "xterm -T -e"; do
+ set $gxti
+ gxtpath="`mywhich $1`"
+ case "$gxtpath" in ?*)
+ gxttitle=$2
+ gxtexec=$3
+ return
+ ;;
+ esac
+ done
+}
+
+# Quotes its argument by inserting '\' in front of every character save
+# for 'A-Za-z0-9/'. Prints the result to stdout.
+quotify() {
+ echo "$1" | sed -e 's/\([^a-zA-Z0-9/]\)/\\\1/g'
+}
+
+ostype=`uname -s`
+if test "$ostype" != "Linux" && test "$ostype" != "SunOS" ; then
+ echo "Linux/Solaris not detected."
+ exit 1
+fi
+
+# The below is GNU-specific. See VBox.sh for the longer Solaris/OS X version.
+TARGET=`readlink -e -- "${0}"` || exit 1
+path="${TARGET%/[!/]*}"
+# 32-bit or 64-bit?
+case `uname -m` in
+ i[3456789]86|x86|i86pc)
+ arch='x86'
+ ;;
+ x86_64|amd64|AMD64)
+ arch='amd64'
+ ;;
+ *)
+ echo "Unknown architecture `uname -m`."
+ exit 1
+ ;;
+esac
+
+# execute the installer
+if test "$ostype" = "Linux"; then
+ for i in "$path/VBoxLinuxAdditions.run" \
+ "$path/VBoxLinuxAdditions-$arch.run"; do
+ if test -f "$i"; then
+ getxterm
+ case "$gxtpath" in ?*)
+ TITLE="VirtualBox Guest Additions installation"
+ BINARY="`quotify "$i"`"
+ exec "$gxtpath" "$gxttitle" "$TITLE" "$gxtexec" /bin/sh "$path/runasroot.sh" --has-terminal "$TITLE" "/bin/sh $BINARY --xwin" "Please try running "\""$i"\"" manually."
+ exit
+ ;;
+ esac
+ fi
+ done
+
+ # else: unknown failure
+ echo "Linux guest additions installer not found -- try to start them manually."
+ exit 1
+
+elif test "$ostype" = "SunOS"; then
+
+ # check for combined package
+ installfile="$path/VBoxSolarisAdditions.pkg"
+ if test -f "$installfile"; then
+
+ # check for pkgadd bin
+ pkgaddbin=pkgadd
+ found=`which pkgadd | grep "no pkgadd"`
+ if test ! -z "$found"; then
+ if test -f "/usr/sbin/pkgadd"; then
+ pkgaddbin=/usr/sbin/pkgadd
+ else
+ echo "Could not find pkgadd."
+ exit 1
+ fi
+ fi
+
+ # check for pfexec
+ pfexecbin=pfexec
+ found=`which pfexec | grep "no pfexec"`
+ if test ! -z "$found"; then
+ # Use su and prompt for password
+ echo "Could not find pfexec."
+ subin=`which su`
+ else
+ idbin=/usr/xpg4/bin/id
+ if test ! -x "$idbin"; then
+ found=`which id 2> /dev/null`
+ if test ! -x "$found"; then
+ echo "Failed to find a suitable user id executable."
+ exit 1
+ else
+ idbin=$found
+ fi
+ fi
+
+ # check if pfexec can get the job done
+ if test "$idbin" = "/usr/xpg4/bin/id"; then
+ userid=`$pfexecbin $idbin -u`
+ else
+ userid=`$pfexecbin $idbin | cut -f1 -d'(' | cut -f2 -d'='`
+ fi
+ if test $userid != "0"; then
+ # pfexec exists but user has no pfexec privileges, switch to using su and prompting password
+ subin=`which su`
+ fi
+ fi
+
+ # create temporary admin file for autoinstall
+ TMPFILE=`mktemp -q /tmp/vbox.XXXXXX`
+ if [ -z $TMPFILE ]; then
+ echo "Unable to create a temporary file"
+ exit 1
+ fi
+ echo "basedir=default
+runlevel=nocheck
+conflict=quit
+setuid=nocheck
+action=nocheck
+partial=quit
+instance=unique
+idepend=quit
+rdepend=quit
+space=quit
+mail=
+" > $TMPFILE
+
+ # check gnome-terminal, use it if it exists.
+ if test -f "/usr/bin/gnome-terminal"; then
+ # use su/pfexec
+ if test -z "$subin"; then
+ /usr/bin/gnome-terminal --title "Installing VirtualBox Additions" --command "/bin/sh -c '$pfexecbin $pkgaddbin -G -d $installfile -n -a $TMPFILE SUNWvboxguest; /bin/echo press ENTER to close this window; /bin/read'"
+ else
+ /usr/bin/gnome-terminal --title "Installing VirtualBox Additions: Root password required." --command "/bin/sh -c '$subin - root -c \"$pkgaddbin -G -d $installfile -n -a $TMPFILE SUNWvboxguest\"; /bin/echo press ENTER to close this window; /bin/read'"
+ fi
+ elif test -f "/usr/X11/bin/xterm"; then
+ # use xterm
+ if test -z "$subin"; then
+ /usr/X11/bin/xterm -title "Installing VirtualBox Additions" -e "$pfexecbin $pkgaddbin -G -d $installfile -n -a $TMPFILE SUNWvboxguest; /bin/echo press ENTER to close this window; /bin/read"
+ else
+ /usr/X11/bin/xterm -title "Installing VirtualBox Additions: Root password required." -e "$subin - root -c \"$pkgaddbin -G -d $installfile -n -a $TMPFILE SUNWvboxguest\"; /bin/echo press ENTER to close this window; /bin/read"
+ fi
+ else
+ echo "No suitable terminal not found. -- install additions using pkgadd -d."
+ fi
+ rm -r $TMPFILE
+
+ exit 0
+ fi
+
+ echo "Solaris guest additions installer not found -- try to start them manually."
+ exit 1
+fi
+
diff --git a/src/VBox/Additions/linux/installer/deffiles b/src/VBox/Additions/linux/installer/deffiles
new file mode 100644
index 00000000..694605f6
--- /dev/null
+++ b/src/VBox/Additions/linux/installer/deffiles
@@ -0,0 +1,84 @@
+# $Id: deffiles $
+## @file
+# VirtualBox linux Guest Additions default files list
+#
+
+#
+# Copyright (C) 2009-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+# This file contains the names of all files that the Guest Additions for
+# Linux guests have ever installed into the main filesystem tree (/usr and
+# /sbin). No entries should ever be removed from this file, as it is used
+# for uninstalling old installations of VirtualBox which did not keep track
+# of the names of their files, or damaged installations which lost track of
+# them.
+
+DEFAULT_FILE_NAMES=" \
+ /usr/bin/vboxadd-xclient \
+ /usr/bin/VBoxClient \
+ /usr/bin/VBoxControl \
+ /usr/sbin/vbox-greeter \
+ /usr/sbin/vboxadd-timesync \
+ /usr/sbin/vboxadd-service \
+ /usr/sbin/VBoxService \
+ /sbin/mount.vboxsf \
+ /usr/lib/VBoxOGLarrayspu.so \
+ /usr/lib64/VBoxOGLarrayspu.so \
+ /usr/lib/VBoxOGLcrutil.so \
+ /usr/lib64/VBoxOGLcrutil.so \
+ /usr/lib/VBoxOGLerrorspu.so \
+ /usr/lib64/VBoxOGLerrorspu.so \
+ /usr/lib/VBoxOGLfeedbackspu.so \
+ /usr/lib64/VBoxOGLfeedbackspu.so \
+ /usr/lib/VBoxOGLpackspu.so \
+ /usr/lib64/VBoxOGLpackspu.so \
+ /usr/lib/VBoxOGLpassthroughspu.so \
+ /usr/lib64/VBoxOGLpassthroughspu.so \
+ /usr/lib/VBoxOGL.so \
+ /usr/lib64/VBoxOGL.so \
+ /usr/share/xgreeters/vbox-greeter.desktop \
+ /etc/X11/Xsession.d/98vboxadd-xclient \
+ /etc/X11/xinit.d/98vboxadd-xclient \
+ /etc/X11/xinit/xinitrc.d/98vboxadd-xclient.sh \
+ /usr/bin/VBoxRandR \
+ /usr/bin/VBoxClient-all \
+ /etc/xdg/autostart/vboxclient.desktop \
+ /usr/share/autostart/vboxclient.desktop \
+ /etc/udev/rules.d/60-vboxadd.rules \
+ /usr/share/xserver-xorg/pci/vboxvideo.ids \
+ /etc/hal/fdi/policy/90-vboxguest.fdi \
+ /etc/udev/rules.d/70-xorg-vboxmouse.rules \
+ /usr/share/hal/fdi/policy/20thirdparty/90-vboxguest.fdi \
+ /usr/lib/X11/xorg.conf.d/50-vboxmouse.conf \
+ /usr/share/X11/xorg.conf.d/50-vboxmouse.conf \
+ /usr/lib/dri/vboxvideo_dri.so \
+ /usr/lib64/dri/vboxvideo_dri.so \
+ /usr/lib64/xorg/modules/drivers/vboxvideo_drv.so \
+ /usr/lib/xorg/modules/drivers/vboxvideo_drv.so \
+ /usr/X11R6/lib64/modules/drivers/vboxvideo_drv.so \
+ /usr/X11R6/lib/modules/drivers/vboxvideo_drv.so \
+ /usr/X11R6/lib/X11/modules/drivers/vboxvideo_drv.so \
+ /usr/lib64/xorg/modules/input/vboxmouse_drv.so \
+ /usr/lib/xorg/modules/input/vboxmouse_drv.so \
+ /usr/X11R6/lib64/modules/input/vboxmouse_drv.so \
+ /usr/X11R6/lib/modules/input/vboxmouse_drv.so \
+ /usr/X11R6/lib/X11/modules/input/vboxmouse_drv.so
+"
+
+DEFAULT_VERSIONED_FILE_NAMES=" \
+ /usr/src/vboxadd \
+ /usr/src/vboxguest \
+ /usr/src/vboxvfs \
+ /usr/src/vboxsf \
+ /usr/src/vboxvideo \
+"
+
diff --git a/src/VBox/Additions/linux/installer/install.sh.in b/src/VBox/Additions/linux/installer/install.sh.in
new file mode 100755
index 00000000..aa76eab3
--- /dev/null
+++ b/src/VBox/Additions/linux/installer/install.sh.in
@@ -0,0 +1,632 @@
+#!/bin/sh
+#
+# Oracle VM VirtualBox
+# VirtualBox Makeself installation starter script
+# for Linux Guest Additions
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+# Testing:
+# * After successful installation, 0 is returned if the vboxguest module version
+# built matches the one loaded and 2 is returned otherwise. E.g. VBoxClient
+# running will prevent vboxguest reloading.
+# * If the kernel modules cannot be built (run the installer with KERN_VER=none)
+# or loaded (run with KERN_VER=<installed non-current version>) then 1 is
+# returned.
+
+PATH=$PATH:/bin:/sbin:/usr/sbin
+
+# Note: These variable names must *not* clash with variables in $CONFIG_DIR/$CONFIG!
+PACKAGE="VBoxGuestAdditions"
+PACKAGE_NAME="VirtualBox Guest Additions"
+UNINSTALL="uninstall.sh"
+PUBLIC_UNINSTALL_HOOK="/usr/sbin/vbox-uninstall-guest-additions"
+ROUTINES="routines.sh"
+INSTALLATION_VER="_VERSION_"
+BUILD_TYPE="_BUILDTYPE_"
+USERNAME="_USERNAME_"
+UNINSTALL_SCRIPTS="vboxadd-x11 vboxvfs vboxadd-timesync vboxadd-service vboxadd"
+
+INSTALLATION_DIR="/opt/$PACKAGE-$INSTALLATION_VER"
+CONFIG_DIR="/var/lib/$PACKAGE"
+CONFIG="config"
+CONFIG_FILES="filelist"
+SELF=$1
+LOGFILE="/var/log/vboxadd-install.log"
+
+## Were we able to stop any previously running Additions kernel modules?
+MODULES_STOPPED=1
+
+. "./$ROUTINES"
+
+check_root
+
+check_deps bzip2 tar
+
+create_log "$LOGFILE"
+
+## @todo r=andy: Explain options like "force" and "no_setup" -- not self-explanatory
+# to the user.
+usage()
+{
+ catinfo << EOF
+
+Usage: $SELF install [<installation directory>]
+ [--with-<module>]
+ [--package-base <base> |
+ uninstall
+ [--force] [--no-setup]
+
+Options:
+ --package-base <base> For use when building distribution packages.
+ Installs relative to <base> instead of to "/",
+ does not run setup, installs to "<base>/usr/lib"
+ instead of to "/opt" and does not create uninstall.
+
+Example:
+$SELF install
+EOF
+ exit 1
+}
+
+# Create a symlink in the filesystem and add it to the list of package files
+add_symlink()
+{
+ self=add_symlink
+ ## Parameters:
+ # The file the link should point to
+ target="$1"
+ # The name of the actual symlink file. Must be an absolute path to a
+ # non-existing file in an existing directory.
+ link="$2"
+ link_dir="`dirname "$link"`"
+ test -n "$target" ||
+ { echo 1>&2 "$self: no target specified"; return 1; }
+ test -d "$link_dir" ||
+ { echo 1>&2 "$self: link directory $link_dir does not exist"; return 1; }
+ expr "$link" : "/.*" > /dev/null ||
+ { echo 1>&2 "$self: link file name is not absolute"; return 1; }
+ rm -f "$link"
+ ln -s "$target" "$link"
+ echo "$link" >> "$CONFIG_DIR/$CONFIG_FILES"
+}
+
+# Create symbolic links targeting all files in a directory in another
+# directory in the filesystem
+link_into_fs()
+{
+ ## Parameters:
+ # Directory containing the link target files
+ target_branch="$1"
+ # Directory to create the link files in
+ directory="$2"
+ for i in "$INSTALLATION_DIR/$target_branch"/*; do
+ test -e "$i" &&
+ add_symlink "$i" "$directory/`basename $i`"
+ done
+}
+
+# Look for broken installations or installations without a known uninstaller
+# and try to clean them up, asking the user first.
+def_uninstall()
+{
+ ## Parameters:
+ # Whether to force cleanup without asking the user
+ force="$1"
+
+ . ./deffiles
+ found=0
+ for i in "/opt/$PACKAGE-"*; do
+ test -e "$i" && found=1
+ done
+ for i in $DEFAULT_FILE_NAMES; do
+ test "$found" = 0 && test -e "$i" && found=1
+ done
+ test "$found" = 0 &&
+ for i in $DEFAULT_VERSIONED_FILE_NAMES; do
+ for j in $i-*; do
+ test "$found" = 0 && test -e "$j" && found=1
+ done
+ done
+ test "$found" = 0 && return 0
+ if ! test "$1" = "force" ; then
+ # Try to make the promised notification appear on next start.
+ VBoxControl guestproperty delete \
+ /VirtualBox/GuestAdd/HostVerLastChecked 2>&1 > /dev/null
+ cat 1>&2 << EOF
+This system appears to have a version of the VirtualBox Guest Additions
+already installed. If it is part of the operating system and kept up-to-date,
+there is most likely no need to replace it. If it is not up-to-date, you
+should get a notification when you start the system. If you wish to replace
+it with this version, please do not continue with this installation now, but
+instead remove the current version first, following the instructions for the
+operating system.
+
+If your system simply has the remains of a version of the Additions you could
+not remove you should probably continue now, and these will be removed during
+installation.
+
+Do you wish to continue? [yes or no]
+EOF
+ read reply dummy
+ if ! expr "$reply" : [yY] > /dev/null &&
+ ! expr "$reply" : [yY][eE][sS] > /dev/null
+ then
+ info
+ info "Cancelling installation."
+ return 1
+ fi
+ fi
+ # Inhibit rebuilding of any installed kernels.
+ for i in /lib/modules/*; do
+ ver="${i##*/}"
+ test ! -d "$i"/build || touch /var/lib/VBoxGuestAdditions/skip-"$ver"
+ done
+ # Stop what we can in the way of services and remove them from the
+ # system
+ for i in $UNINSTALL_SCRIPTS; do
+ stop_init_script "$i" 2>> "${LOGFILE}"
+ test -z "$NO_CLEANUP" && test -x "./$i" && "./$i" cleanup 1>&2 2>> "$LOGFILE"
+ delrunlevel "$i" 2>> "${LOGFILE}"
+ remove_init_script "$i" 2>> "${LOGFILE}"
+ done
+ for i in "/opt/$PACKAGE-"*/init; do
+ for j in $UNINSTALL_SCRIPTS; do
+ script="${i}/${j}"
+ test -x "${script}" && test -z "$NO_CLEANUP" &&
+ grep -q '^# *cleanup_script *$' "${script}" &&
+ "${script}" cleanup 1>&2 2>> "$LOGFILE"
+ done
+ done
+
+ # Get rid of any remaining files
+ for i in $DEFAULT_FILE_NAMES; do
+ rm -f "$i" 2> /dev/null
+ done
+ for i in $DEFAULT_VERSIONED_FILE_NAMES; do
+ rm -f "$i-"* 2> /dev/null
+ done
+ rm -f "/usr/lib/$PACKAGE" "/usr/lib64/$PACKAGE" "/usr/share/$PACKAGE" \
+ "/usr/lib/i386-linux-gnu/$PACKAGE" "/usr/lib/x86_64-linux-gnu/$PACKAGE"
+
+ # And any packages left under /opt
+ for i in "/opt/$PACKAGE-"*; do
+ test -d "$i" && rm -rf "$i"
+ done
+ return 0
+}
+
+info "$PACKAGE_NAME installer"
+
+# Sensible default actions
+ACTION="install"
+DO_SETUP="true"
+NO_CLEANUP=""
+FORCE_UPGRADE=""
+PACKAGE_BASE=""
+
+while [ $# -ge 2 ];
+do
+ ARG=$2
+ shift
+
+ if [ -z "$MY_END_OF_OPTIONS" ]; then
+ case "$ARG" in
+
+ install)
+ ACTION="install"
+ ;;
+
+ uninstall)
+ ACTION="uninstall"
+ ;;
+
+ package)
+ ACTION="package"
+ INSTALLATION_DIR=/usr/lib
+ PACKAGE_BASE="$2"
+ DO_SETUP=""
+ shift
+ if test ! -d "${PACKAGE_BASE}"; then
+ info "Package base directory not found."
+ usage
+ fi
+ ;;
+
+ ## @todo Add per-module options handling, e.g. --lightdm-greeter-dir
+ # or --lightdm-config
+
+ ## @todo Add listing all available modules (+ their options, e.g.
+ # with callback mod_mymod_show_options?)
+
+ --with-*)
+ MODULE_CUR=`expr "$ARG" : '--with-\(.*\)'`
+ # Check if corresponding module in installer/module-$1 exists.
+ # Note: Module names may not contain spaces or other funny things.
+ if [ ! -f "./installer/module-${MODULE_CUR}" ]; then
+ info "Error: Module \"${MODULE_CUR}\" does not exist."
+ usage
+ fi
+ # Give the module the chance of doing initialization work / checks.
+ . "./installer/module-${MODULE_CUR}"
+ mod_${MODULE_CUR}_init
+ if test $? -ne 0; then
+ echo 1>&2 "Module '${MODULE_CUR}' failed to initialize"
+ if ! test "$FORCE_UPGRADE" = "force"; then
+ return 1
+ fi
+ # Continue initialization.
+ fi
+ # Add module to the list of modules to handle later.
+ if test -z "${INSTALLATION_MODULES_LIST}"; then
+ INSTALLATION_MODULES_LIST="${MODULE_CUR}"
+ else
+ INSTALLATION_MODULES_LIST="${INSTALLATION_MODULES_LIST} ${MODULE_CUR}"
+ fi
+ shift
+ ;;
+
+ --force|force) # Keep "force" for backwards compatibility.
+ FORCE_UPGRADE="force"
+ ;;
+
+ --no-setup|no_setup) # Keep "no_setup" for backwards compatibility.
+ DO_SETUP=""
+ ;;
+
+ --no-cleanup|no_cleanup) # Keep "no_cleanup" for backwards compatibility.
+ # Do not do cleanup of old modules when removing them. For
+ # testing purposes only.
+ DO_SETUP=""
+ NO_CLEANUP="no_cleanup"
+ ;;
+
+ --)
+ MY_END_OF_OPTIONS="1"
+ ;;
+
+ *)
+ if [ "`echo $1|cut -c1`" != "/" ]; then
+ info "Please specify an absolute path"
+ usage
+ fi
+ INSTALLATION_DIR="$1"
+ shift
+ ;;
+ esac
+ fi
+done
+
+# Check architecture
+cpu=`uname -m`;
+case "$cpu" in
+ i[3456789]86|x86)
+ cpu="x86"
+ lib_candidates="/usr/lib/i386-linux-gnu /usr/lib /lib"
+ ;;
+ x86_64|amd64)
+ cpu="amd64"
+ lib_candidates="/usr/lib/x86_64-linux-gnu /usr/lib64 /usr/lib /lib64 /lib"
+ ;;
+ *)
+ cpu="unknown"
+esac
+ARCH_PACKAGE="$PACKAGE-$cpu.tar.bz2"
+if [ ! -r "$ARCH_PACKAGE" ]; then
+ info "Detected unsupported $cpu machine type."
+ exit 1
+fi
+# Find the most appropriate libary folder by seeing which of the candidate paths
+# are actually in the shared linker path list and choosing the first. We look
+# for Debian-specific paths first, then LSB ones, then the new RedHat ones.
+libs=`ldconfig -v 2>/dev/null | grep -v ^$'\t'`
+for i in $lib_candidates; do
+ if echo $libs | grep -q $i; then
+ lib_path=$i
+ break
+ fi
+done
+if [ ! -x "$lib_path" ]; then
+ info "Unable to determine correct library path."
+ exit 1
+fi
+
+# uninstall any previous installation
+# If the currently installed Additions have provided an uninstallation hook, try
+# that first.
+if test -x "${PUBLIC_UNINSTALL_HOOK}"; then
+ "${PUBLIC_UNINSTALL_HOOK}" 1>&2 ||
+ abort "Failed to remove existing installation. Aborting..."
+fi
+
+INSTALL_DIR=""
+uninstalled=0
+test -r "$CONFIG_DIR/$CONFIG" &&
+ eval `grep ^INSTALL_DIR= "$CONFIG_DIR/$CONFIG"` 2>/dev/null &&
+ eval `grep ^UNINSTALLER= "$CONFIG_DIR/$CONFIG"` 2>/dev/null
+if test -n "$INSTALL_DIR" -a -x "$INSTALL_DIR/$UNINSTALLER"; then
+ "$INSTALL_DIR/$UNINSTALLER" $NO_CLEANUP 1>&2 ||
+ abort "Failed to remove existing installation. Aborting..."
+ uninstalled=1
+fi
+test "$uninstalled" = 0 && def_uninstall "$FORCE_UPGRADE" && uninstalled=1
+test "$uninstalled" = 0 && exit 1
+rm -f "$CONFIG_DIR/$CONFIG"
+rm -f "$CONFIG_DIR/$CONFIG_FILES"
+rmdir "$CONFIG_DIR" 2>/dev/null
+test "$ACTION" = "install" || exit 0
+
+# Now check whether the kernel modules were stopped.
+lsmod | grep -q vboxguest && MODULES_STOPPED=
+
+# Choose a proper umask
+umask 022
+
+# Set installer modules directory
+INSTALLATION_MODULES_DIR="$INSTALLATION_DIR/installer/"
+
+# install the new version
+mkdir -p -m 755 "$CONFIG_DIR"
+test ! -d "$INSTALLATION_DIR" && REMOVE_INSTALLATION_DIR=1
+mkdir -p -m 755 "$INSTALLATION_DIR"
+
+# install and load installer modules
+if [ -d installer ]; then
+ info "Copying additional installer modules ..."
+ mkdir -p -m 755 "$INSTALLATION_MODULES_DIR"
+ for CUR_FILE in `ls installer/*`; do
+ install -p -m 755 "$CUR_FILE" "$INSTALLATION_MODULES_DIR"
+ if [ $? -ne 0 ]; then
+ info "Error: Failed to copy installer module \"$CUR_FILE\""
+ if ! test "$FORCE_UPGRADE" = "force"; then
+ exit 1
+ fi
+ fi
+ done
+fi
+
+# Create a list of the files in the archive, skipping any directories which
+# already exist in the filesystem.
+bzip2 -d -c "$ARCH_PACKAGE" | tar -tf - |
+ while read name; do
+ fullname="$INSTALLATION_DIR/$name"
+ case "$fullname" in
+ */)
+ test ! -d "$fullname" &&
+ echo "$fullname" >> "$CONFIG_DIR/$CONFIG_FILES"
+ ;;
+ *)
+ echo "$fullname" >> "$CONFIG_DIR/$CONFIG_FILES"
+ ;;
+ esac
+ done
+bzip2 -d -c "$ARCH_PACKAGE" | tar -xf - -C "$INSTALLATION_DIR" || exit 1
+
+# Set symlinks into /usr and /sbin
+link_into_fs "bin" "${PACKAGE_BASE}/usr/bin"
+link_into_fs "sbin" "${PACKAGE_BASE}/usr/sbin"
+link_into_fs "lib" "$lib_path"
+link_into_fs "src" "${PACKAGE_BASE}/usr/src"
+
+if [ -d "$INSTALLATION_MODULES_DIR" ]; then
+ info "Installing additional modules ..."
+ for CUR_MODULE in `find "$INSTALLATION_MODULES_DIR" 2>/dev/null`
+ do
+ echo "$CUR_MODULE" >> "$CONFIG_DIR/$CONFIG_FILES"
+ done
+fi
+
+for CUR_MODULE in ${INSTALLATION_MODULES_LIST}
+do
+ mod_${CUR_MODULE}_install
+ if [ $? -ne 0 ]; then
+ info "Error: Failed to install module \"$CUR_MODULE\""
+ if ! test "$FORCE_UPGRADE" = "force"; then
+ exit 1
+ fi
+ fi
+done
+
+# Remember our installation configuration before we call any init scripts
+cat > "$CONFIG_DIR/$CONFIG" << EOF
+# $PACKAGE installation record.
+# Package installation directory
+INSTALL_DIR='$INSTALLATION_DIR'
+# Additional installation modules
+INSTALL_MODULES_DIR='$INSTALLATION_MODULES_DIR'
+INSTALL_MODULES_LIST='$INSTALLATION_MODULES_LIST'
+# Package uninstaller. If you repackage this software, please make sure
+# that this prints a message and returns an error so that the default
+# uninstaller does not attempt to delete the files installed by your
+# package.
+UNINSTALLER='$UNINSTALL'
+# Package version
+INSTALL_VER='$INSTALLATION_VER'
+# Build type and user name for logging purposes
+BUILD_TYPE='$BUILD_TYPE'
+USERNAME='$USERNAME'
+EOF
+
+# Give the modules the chance to write their stuff
+# to the installation config as well.
+if [ -n "${INSTALLATION_MODULES_LIST}" ]; then
+ info "Saving modules configuration ..."
+ for CUR_MODULE in ${INSTALLATION_MODULES_LIST}
+ do
+ echo "`mod_${CUR_MODULE}_config_save`" >> "$CONFIG_DIR/$CONFIG"
+ done
+fi
+
+"$INSTALLATION_DIR/init/vboxadd" setup 1>&2 2>> "${LOGFILE}"
+
+# Install, set up and start init scripts
+install_init_script "$INSTALLATION_DIR"/init/vboxadd vboxadd 2>> "$LOGFILE"
+install_init_script "$INSTALLATION_DIR"/init/vboxadd-service vboxadd-service \
+ 2>> "$LOGFILE"
+finish_init_script_install
+addrunlevel vboxadd 2>> "$LOGFILE"
+addrunlevel vboxadd-service 2>> "$LOGFILE"
+start_init_script vboxadd 2>> "$LOGFILE"
+start_init_script vboxadd-service 2>> "$LOGFILE"
+
+cp $ROUTINES $INSTALLATION_DIR
+echo $INSTALLATION_DIR/$ROUTINES >> "$CONFIG_DIR/$CONFIG_FILES"
+cat > $INSTALLATION_DIR/$UNINSTALL << EOF
+#!/bin/sh
+# Auto-generated uninstallation file
+
+PATH=\$PATH:/bin:/sbin:/usr/sbin
+LOGFILE="/var/log/vboxadd-uninstall.log"
+
+# Read routines.sh
+if ! test -r "$INSTALLATION_DIR/$ROUTINES"; then
+ echo 1>&2 "Required file $ROUTINES not found. Aborting..."
+ return 1
+fi
+. "$INSTALLATION_DIR/$ROUTINES"
+
+# We need to be run as root
+check_root
+
+create_log "\$LOGFILE"
+
+echo 1>&2 "Removing installed version $INSTALLATION_VER of $PACKAGE_NAME..."
+
+NO_CLEANUP=""
+if test "\$1" = "no_cleanup"; then
+ shift
+ NO_CLEANUP="no_cleanup"
+fi
+
+test -r "$CONFIG_DIR/$CONFIG_FILES" || abort "Required file $CONFIG_FILES not found. Aborting..."
+
+# Stop and clean up all services
+if test -r "$INSTALLATION_DIR"/init/vboxadd-service; then
+ stop_init_script vboxadd-service 2>> "\$LOGFILE"
+ delrunlevel vboxadd-service 2>> "\$LOGFILE"
+ remove_init_script vboxadd-service 2>> "\$LOGFILE"
+fi
+if test -r "$INSTALLATION_DIR"/init/vboxadd; then
+ stop_init_script vboxadd 2>> "\$LOGFILE"
+ test -n "\$NO_CLEANUP" || "$INSTALLATION_DIR"/init/vboxadd cleanup 2>> "\$LOGFILE"
+ delrunlevel vboxadd 2>> "\$LOGFILE"
+ remove_init_script vboxadd 2>> "\$LOGFILE"
+fi
+finish_init_script_install
+
+# Load all modules
+# Important: This needs to be done before loading the configuration
+# value below to not override values which are set to a default
+# value in the modules itself.
+for CUR_MODULE in `find "$INSTALLATION_MODULES_DIR" -name "module-*" 2>/dev/null`
+ do
+ . "\$CUR_MODULE"
+ done
+
+# Load configuration values
+test -r "$CONFIG_DIR/$CONFIG" && . "$CONFIG_DIR/$CONFIG"
+
+# Call uninstallation initialization of all modules
+for CUR_MODULE in "$INSTALLATION_MODULES_LIST"
+ do
+ if test -z "\$CUR_MODULE"; then
+ continue
+ fi
+ mod_\${CUR_MODULE}_pre_uninstall
+ if [ $? -ne 0 ]; then
+ echo 1>&2 "Module \"\$CUR_MODULE\" failed to initialize uninstallation"
+ # Continue initialization.
+ fi
+ done
+
+# Call uninstallation of all modules
+for CUR_MODULE in "$INSTALLATION_MODULES_LIST"
+ do
+ if test -z "\$CUR_MODULE"; then
+ continue
+ fi
+ mod_\${CUR_MODULE}_uninstall
+ if [ $? -ne 0 ]; then
+ echo 1>&2 "Module \"\$CUR_MODULE\" failed to uninstall"
+ # Continue uninstallation.
+ fi
+ done
+
+# And remove all files and empty installation directories
+# Remove any non-directory entries
+cat "$CONFIG_DIR/$CONFIG_FILES" | xargs rm 2>/dev/null
+# Remove any empty (of files) directories in the file list
+cat "$CONFIG_DIR/$CONFIG_FILES" |
+ while read file; do
+ case "\$file" in
+ */)
+ test -d "\$file" &&
+ find "\$file" -depth -type d -exec rmdir '{}' ';' 2>/dev/null
+ ;;
+ esac
+ done
+
+# Remove configuration files
+rm "$CONFIG_DIR/$CONFIG_FILES" 2>/dev/null
+rm "$CONFIG_DIR/$CONFIG" 2>/dev/null
+rmdir "$CONFIG_DIR" 2>/dev/null
+exit 0
+EOF
+chmod 0755 $INSTALLATION_DIR/$UNINSTALL
+echo $INSTALLATION_DIR/$UNINSTALL >> "$CONFIG_DIR/$CONFIG_FILES"
+test -n "$REMOVE_INSTALLATION_DIR" &&
+ echo "$INSTALLATION_DIR/" >> "$CONFIG_DIR/$CONFIG_FILES"
+
+cat > "${PUBLIC_UNINSTALL_HOOK}" << EOF
+#!/bin/sh
+# This executable provides a well-known way to uninstall VirtualBox Guest
+# Additions in order to re-install them from a different source. A common case
+# is uninstalling distribution-provide Additions to install the version provided
+# by VirtualBox. Distributions should put the right command in here to do the
+# removal, e.g. "dnf remove VirtualBox-guest-additions". Leaving kernel modules
+# provided by the distribution kernel package in place is acceptable if the
+# location does not clash with the VirtualBox-provided module location (misc).
+$INSTALLATION_DIR/$UNINSTALL
+EOF
+chmod 0755 "${PUBLIC_UNINSTALL_HOOK}"
+echo "$PUBLIC_UNINSTALL_HOOK" >> "$CONFIG_DIR/$CONFIG_FILES"
+
+# Test for a problem with old Mesa versions which stopped our 3D libraries
+# from working. Known to affect Debian 7.11, probably OL/RHEL 5.
+# The problem was that the system Mesa library had an ABI note, which caused
+# ldconfig to always prefer it to ours.
+if ldconfig -p | grep -q "libGL.so.1.*Linux 2.4"; then
+ gl_with_abi=`ldconfig -p | grep "libGL.so.1.*Linux 2.4" | sed 's/.*=> //'`
+ cat >&2 << EOF
+This system appears to be running a version of Mesa with a known problem
+which will prevent VirtualBox 3D pass-through from working. See
+ https://bugs.freedesktop.org/show_bug.cgi?id=26663
+The following, run as root should fix this, though you will have to run it
+again if the system version of Mesa is updated:
+EOF
+ for i in ${gl_with_abi}; do
+ echo >&2 " strip -R .note.ABI-tag ${i}"
+ done
+ echo >&2 " ldconfig"
+fi
+
+# And do a final test as to whether the kernel modules were properly created
+# and loaded. Return 0 if both are true, 1 if the modules could not be built
+# or loaded (except due to already running older modules) and 2 if already
+# running modules probably prevented new ones from loading. vboxvideo is
+# currently not tested.
+# Assume that we have already printed enough messages by now and stay silent.
+modinfo vboxguest >/dev/null 2>&1 || exit 1
+lsmod | grep -q vboxguest || exit 1
+test -n "${MODULES_STOPPED}" || exit 2
+exit 0
diff --git a/src/VBox/Additions/linux/installer/module-autologon.sh b/src/VBox/Additions/linux/installer/module-autologon.sh
new file mode 100644
index 00000000..46f8e333
--- /dev/null
+++ b/src/VBox/Additions/linux/installer/module-autologon.sh
@@ -0,0 +1,172 @@
+# Oracle VM VirtualBox
+# $Id: module-autologon.sh $
+## @file
+# VirtualBox Linux Guest Additions installer - autologon module
+#
+
+#
+# Copyright (C) 2012-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+# @todo Document functions and their usage!
+
+MOD_AUTOLOGON_DEFAULT_LIGHTDM_CONFIG="/etc/lightdm/lightdm.conf"
+MOD_AUTOLOGON_DEFAULT_LIGHTDM_GREETER_DIR="/usr/share/xgreeters"
+
+mod_autologon_init()
+{
+ echo "Initializing auto-logon support ..."
+ return 0
+}
+
+mod_autologon_install_ex()
+{
+ info "Installing auto-logon support ..."
+
+ ## Parameters:
+ # Greeter directory. Defaults to /usr/share/xgreeters.
+ greeter_dir="$1"
+ # LightDM config. Defaults to /etc/lightdm/lightdm.conf.
+ lightdm_config="$2"
+ # Whether to force installation if non-compatible distribution
+ # is detected.
+ force="$3"
+
+ # Check for Ubuntu and derivates. @todo Debian?
+ distros="Ubuntu UbuntuStudio Edubuntu Kubuntu Lubuntu Mythbuntu Xubuntu"
+ ## @todo Map Linux Mint versions to Ubuntu ones.
+
+ ## @todo Move the distro check to a routine / globals as soon as
+ ## we have other distribution-dependent stuff.
+ which lsb_release &>/dev/null
+ if test "$?" -ne "0"; then
+ info "Error: lsb_release not found (path set?), skipping auto-logon installation"
+ return 1
+ fi
+ distro_name=$(lsb_release -si)
+ distro_ver=$(lsb_release -sr)
+
+ for distro_cur in ${distros}; do
+ if test "$distro_name" = "$distro_cur"; then
+ distro_found="true"
+ break
+ fi
+ done
+
+ if test -z "$distro_found"; then
+ if ! test "$force" = "force"; then
+ info "Error: Unsupported distribution \"$distro_name\" found, skipping auto-logon installation"
+ return 1
+ fi
+ info "Warning: Unsupported distribution \"$distro_name\" found"
+ else
+ # Do we have Ubuntu 11.10 or greater?
+ # Use AWK for comparison since we run on plan sh.
+ echo | awk 'END { exit ( !('"$distro_ver >= 11.10"') ); }'
+ if test "$?" -ne "0"; then
+ if ! test "$force" = "force"; then
+ info "Error: Version $distro_ver of \"$distro_name\" not supported, skipping auto-logon installation"
+ return 1
+ fi
+ info "Warning: Unsupported \"$distro_name\" version $distro_ver found"
+ fi
+ fi
+
+ # Install dependencies (lightdm and FLTK 1.3+) using apt-get.
+ which apt-get &>/dev/null
+ if test "$?" -ne "0"; then
+ info "Error: apt-get not found (path set?), skipping auto-logon installation"
+ return 1
+ fi
+ info "Checking and installing necessary dependencies ..."
+ apt-get -qqq -y install libfltk1.3 libfltk-images1.3 || return 1
+ apt-get -qqq -y install lightdm || return 1
+
+ # Check for LightDM config.
+ if ! test -f "$lightdm_config"; then
+ info "Error: LightDM config \"$lightdm_config\" not found (LightDM installed?), skipping auto-logon installation"
+ return 1
+ fi
+
+ # Check for /usr/share/xgreeters.
+ if ! test -d "$greeter_dir"; then
+ if ! test "$force" = "force"; then
+ info "Error: Directory \"$greeter_dir\" does not exist, skipping auto-logon installation"
+ return 1
+ fi
+ info "Warning: Directory \"$greeter_dir\" does not exist, creating it"
+ mkdir -p -m 755 "$greeter_dir" || return 1
+ fi
+
+ # Link to required greeter files into $greeter_dir.
+ add_symlink "$INSTALLATION_DIR/other/vbox-greeter.desktop" "$greeter_dir/vbox-greeter.desktop"
+
+ # Backup and activate greeter config.
+ if ! test -f "$lightdm_config.vbox-backup"; then
+ info "Backing up LightDM configuration file ..."
+ cp "$lightdm_config" "$lightdm_config.vbox-backup" || return 1
+ chmod 644 "$lightdm_config.vbox-backup" || return 1
+ fi
+ sed -i -e 's/^\s*greeter-session\s*=.*/greeter-session=vbox-greeter/g' "$lightdm_config" || return 1
+ chmod 644 "$lightdm_config" || return 1
+
+ info "Auto-logon installation successful"
+ return 0
+}
+
+mod_autologon_install()
+{
+ if [ -z "$MOD_AUTOLOGON_LIGHTDM_GREETER_DIR" ]; then
+ MOD_AUTOLOGON_LIGHTDM_GREETER_DIR=$MOD_AUTOLOGON_DEFAULT_LIGHTDM_GREETER_DIR
+ fi
+ if [ -z "$MOD_AUTOLOGON_LIGHTDM_CONFIG" ]; then
+ MOD_AUTOLOGON_LIGHTDM_CONFIG=$MOD_AUTOLOGON_DEFAULT_LIGHTDM_CONFIG
+ fi
+
+ mod_autologon_install_ex "$MOD_AUTOLOGON_LIGHTDM_GREETER_DIR" "$MOD_AUTOLOGON_LIGHTDM_CONFIG" "$MOD_AUTOLOGON_FORCE"
+ return $?
+}
+
+mod_autologon_pre_uninstall()
+{
+ echo "Preparing to uninstall auto-logon support ..."
+ return 0
+}
+
+mod_autologon_uninstall()
+{
+ if test -z "$MOD_AUTOLOGON_LIGHTDM_CONFIG"; then
+ return 0
+ fi
+ info "Un-installing auto-logon support ..."
+
+ # Switch back to original greeter.
+ if test -f "$MOD_AUTOLOGON_LIGHTDM_CONFIG.vbox-backup"; then
+ mv "$MOD_AUTOLOGON_LIGHTDM_CONFIG.vbox-backup" "$MOD_AUTOLOGON_LIGHTDM_CONFIG"
+ if test "$?" -ne "0"; then
+ info "Warning: Could not restore original LightDM config \"$MOD_AUTOLOGON_LIGHTDM_CONFIG\""
+ fi
+ fi
+
+ # Remove greeter directory (if not empty).
+ rm "$MOD_AUTOLOGON_LIGHTDM_GREETER_DIR" 2>/dev/null
+
+ info "Auto-logon uninstallation successful"
+ return 0
+}
+
+mod_autologon_config_save()
+{
+ echo "
+MOD_AUTOLOGON_LIGHTDM_CONFIG='$MOD_AUTOLOGON_LIGHTDM_CONFIG'
+MOD_AUTOLOGON_LIGHTDM_GREETER_DIR='$MOD_AUTOLOGON_LIGHTDM_GREETER_DIR'"
+}
+
diff --git a/src/VBox/Additions/linux/installer/vboxadd-service.sh b/src/VBox/Additions/linux/installer/vboxadd-service.sh
new file mode 100755
index 00000000..5d6c5525
--- /dev/null
+++ b/src/VBox/Additions/linux/installer/vboxadd-service.sh
@@ -0,0 +1,159 @@
+#!/bin/sh
+# $Id: vboxadd-service.sh $
+## @file
+# Linux Additions Guest Additions service daemon init script.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+# X-Conflicts-With is our own invention, which we use when converting to
+# a systemd unit.
+
+# chkconfig: 345 35 65
+# description: VirtualBox Additions service
+#
+### BEGIN INIT INFO
+# Provides: vboxadd-service
+# Required-Start: vboxadd
+# Required-Stop: vboxadd
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# X-Conflicts-With: systemd-timesyncd.service
+# Description: VirtualBox Additions Service
+### END INIT INFO
+
+PATH=$PATH:/bin:/sbin:/usr/sbin
+SCRIPTNAME=vboxadd-service.sh
+
+PIDFILE="/var/run/${SCRIPTNAME}"
+
+# Preamble for Gentoo
+if [ "`which $0`" = "/sbin/rc" ]; then
+ shift
+fi
+
+begin()
+{
+ test -n "${2}" && echo "${SCRIPTNAME}: ${1}."
+ logger -t "${SCRIPTNAME}" "${1}."
+}
+
+succ_msg()
+{
+ logger -t "${SCRIPTNAME}" "${1}."
+}
+
+fail_msg()
+{
+ echo "${SCRIPTNAME}: ${1}." >&2
+ logger -t "${SCRIPTNAME}" "${1}."
+}
+
+daemon() {
+ $1 $2 $3
+}
+
+killproc() {
+ killall $1
+ rm -f $PIDFILE
+}
+
+if which start-stop-daemon >/dev/null 2>&1; then
+ daemon() {
+ start-stop-daemon --start --exec $1 -- $2 $3
+ }
+
+ killproc() {
+ start-stop-daemon --stop --retry 2 --exec $@
+ }
+fi
+
+binary=/usr/sbin/VBoxService
+
+testbinary() {
+ test -x "$binary" || {
+ echo "Cannot run $binary"
+ exit 1
+ }
+}
+
+vboxaddrunning() {
+ lsmod | grep -q "vboxguest[^_-]"
+}
+
+start() {
+ if ! test -f $PIDFILE; then
+ begin "Starting VirtualBox Guest Addition service" console;
+ vboxaddrunning || {
+ echo "VirtualBox Additions module not loaded!"
+ exit 1
+ }
+ testbinary
+ daemon $binary --pidfile $PIDFILE > /dev/null
+ RETVAL=$?
+ succ_msg "VirtualBox Guest Addition service started"
+ fi
+ return $RETVAL
+}
+
+stop() {
+ if test -f $PIDFILE; then
+ begin "Stopping VirtualBox Guest Addition service" console;
+ killproc $binary
+ RETVAL=$?
+ if ! pidof VBoxService > /dev/null 2>&1; then
+ rm -f $PIDFILE
+ succ_msg "VirtualBox Guest Addition service stopped"
+ else
+ fail_msg "VirtualBox Guest Addition service failed to stop"
+ fi
+ fi
+ return $RETVAL
+}
+
+restart() {
+ stop && start
+}
+
+status() {
+ echo -n "Checking for VBoxService"
+ if [ -f $PIDFILE ]; then
+ echo " ...running"
+ else
+ echo " ...not running"
+ fi
+}
+
+case "$1" in
+start)
+ start
+ ;;
+stop)
+ stop
+ ;;
+restart)
+ restart
+ ;;
+status)
+ status
+ ;;
+setup)
+ ;;
+cleanup)
+ ;;
+*)
+ echo "Usage: $0 {start|stop|restart|status}"
+ exit 1
+esac
+
+exit $RETVAL
diff --git a/src/VBox/Additions/linux/installer/vboxadd-x11.sh b/src/VBox/Additions/linux/installer/vboxadd-x11.sh
new file mode 100755
index 00000000..6b6787b0
--- /dev/null
+++ b/src/VBox/Additions/linux/installer/vboxadd-x11.sh
@@ -0,0 +1,613 @@
+#! /bin/sh
+# $Id: vboxadd-x11.sh $
+## @file
+# Linux Additions X11 setup init script ($Revision: 127855 $)
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+
+# chkconfig: 35 30 70
+# description: VirtualBox Linux Additions kernel modules
+#
+### BEGIN INIT INFO
+# Provides: vboxadd-x11
+# Required-Start:
+# Required-Stop:
+# Default-Start:
+# Default-Stop:
+# Description: VirtualBox Linux Additions X11 setup
+### END INIT INFO
+
+PATH=$PATH:/bin:/sbin:/usr/sbin
+CONFIG_DIR="/var/lib/VBoxGuestAdditions"
+CONFIG="${CONFIG_DIR}/config"
+MODPROBE=/sbin/modprobe
+
+if $MODPROBE -c 2>/dev/null | grep -q '^allow_unsupported_modules *0'; then
+ MODPROBE="$MODPROBE --allow-unsupported-modules"
+fi
+
+# Find the version of X installed
+# The last of the three is for the X.org 6.7 included in Fedora Core 2
+xver=`X -version 2>&1`
+x_version=`echo "$xver" | sed -n 's/^X Window System Version \([0-9.]\+\)/\1/p'``echo "$xver" | sed -n 's/^XFree86 Version \([0-9.]\+\)/\1/p'``echo "$xver" | sed -n 's/^X Protocol Version 11, Revision 0, Release \([0-9.]\+\)/\1/p'``echo "$xver" | sed -n 's/^X.Org X Server \([0-9.]\+\)/\1/p'`
+x_version_short=`echo "${x_version}" | sed 's/\([0-9]*\.[0-9]*\)\..*/\1/'`
+# Version of Redhat or Fedora installed. Needed for setting up selinux policy.
+redhat_release=`cat /etc/redhat-release 2> /dev/null`
+# Version of OL installed. Needed for blacklisting vboxvideo.
+oracle_release=`cat /etc/oracle-release 2> /dev/null`
+# All the different possible locations for XFree86/X.Org configuration files
+# - how many of these have ever been used?
+x11conf_files="/etc/X11/xorg.conf /etc/X11/xorg.conf-4 /etc/X11/.xorg.conf \
+ /etc/xorg.conf /usr/etc/X11/xorg.conf-4 /usr/etc/X11/xorg.conf \
+ /usr/lib/X11/xorg.conf-4 /usr/lib/X11/xorg.conf /etc/X11/XF86Config-4 \
+ /etc/X11/XF86Config /etc/XF86Config /usr/X11R6/etc/X11/XF86Config-4 \
+ /usr/X11R6/etc/X11/XF86Config /usr/X11R6/lib/X11/XF86Config-4 \
+ /usr/X11R6/lib/X11/XF86Config"
+
+# Preamble for Gentoo
+if [ "`which $0`" = "/sbin/rc" ]; then
+ shift
+fi
+
+dev=/dev/vboxguest
+userdev=/dev/vboxuser
+owner=vboxadd
+group=1
+
+fail()
+{
+ echo "${1}" >&2
+ exit 1
+}
+
+# Install an X11 desktop startup application. The application should be a shell script.
+# Its name should be purely alphanumeric and should start with a two digit number (preferably
+# 98 or thereabouts) to indicate its place in the Debian Xsession startup order.
+#
+# syntax: install_x11_startup_app app desktop service_name
+install_x11_startup_app() {
+ self="install_x11_startup_app"
+ app_src=$1
+ desktop_src=$2
+ service_name=$3
+ alt_command=$4
+ test -r "$app_src" || fail "$self: no script given"
+ test -r "$desktop_src" || fail "$self: no desktop file given"
+ test -n "$service_name" || fail "$self: no service given"
+ test -n "$alt_command" || fail "$self: no service given"
+ app_dest=`basename $app_src sh`
+ app_dest_sh=`basename $app_src sh`.sh
+ desktop_dest=`basename $desktop_src`
+ found=0
+ x11_autostart="/etc/xdg/autostart"
+ kde_autostart="/usr/share/autostart"
+ redhat_dir=/etc/X11/Xsession.d
+ mandriva_dir=/etc/X11/xinit.d
+ debian_dir=/etc/X11/xinit/xinitrc.d
+ if [ -d "$mandriva_dir" -a -w "$mandriva_dir" -a -x "$mandriva_dir" ]
+ then
+ install -m 0644 $app_src "$mandriva_dir/$app_dest"
+ found=1
+ fi
+ if [ -d "$x11_autostart" -a -w "$x11_autostart" -a -x "$x11_autostart" ]
+ then
+ install -m 0644 $desktop_src "$x11_autostart/$desktop_dest"
+ found=1
+ fi
+ if [ -d "$kde_autostart" -a -w "$kde_autostart" -a -x "$kde_autostart" ]
+ then
+ install -m 0644 $desktop_src "$kde_autostart/$desktop_dest"
+ found=1
+ fi
+ if [ -d "$redhat_dir" -a -w "$redhat_dir" -a -x "$redhat_dir" ]
+ then
+ install -m 0644 $app_src "$redhat_dir/$app_dest"
+ found=1
+ fi
+ if [ -d "$debian_dir" -a -w "$debian_dir" -a -x "$debian_dir" ]
+ then
+ install -m 0755 $app_src "$debian_dir/$app_dest_sh"
+ found=1
+ fi
+ if [ $found -eq 1 ]; then
+ return 0
+ fi
+ cat >&2 << EOF
+Could not set up the $service_name desktop service.
+To start it at log-in for a given user, add the command $alt_command
+to the file .xinitrc in their home directory.
+EOF
+ return 1
+}
+
+
+start()
+{
+ # Todo: check configuration and correct it if necessary
+ return 0
+}
+
+stop()
+{
+ return 0
+}
+
+restart()
+{
+ stop && start
+ return 0
+}
+
+setup_opengl()
+{
+ # Install the guest OpenGL drivers. For now we don't support
+ # multi-architecture installations
+ rm -f /etc/ld.so.conf.d/00vboxvideo.conf
+ rm -Rf /var/lib/VBoxGuestAdditions/lib
+ if /usr/bin/VBoxClient --check3d 2>/dev/null; then
+ mkdir -p /var/lib/VBoxGuestAdditions/lib
+ ln -sf "${INSTALL_DIR}/lib/VBoxOGL.so" /var/lib/VBoxGuestAdditions/lib/libGL.so.1
+ # SELinux for the OpenGL libraries, so that gdm can load them during the
+ # acceleration support check. This prevents an "Oh no, something has gone
+ # wrong!" error when starting EL7 guests.
+ if test -e /etc/selinux/config; then
+ if command -v semanage > /dev/null; then
+ semanage fcontext -a -t lib_t "/var/lib/VBoxGuestAdditions/lib/libGL.so.1"
+ fi
+ chcon -h -t lib_t "/var/lib/VBoxGuestAdditions/lib/libGL.so.1"
+ fi
+ echo "/var/lib/VBoxGuestAdditions/lib" > /etc/ld.so.conf.d/00vboxvideo.conf
+ fi
+ ldconfig
+}
+
+setup()
+{
+ if test -r "${CONFIG}"; then
+ . "${CONFIG}"
+ else
+ fail "Configuration file ${CONFIG} not found"
+ fi
+ test -n "$INSTALL_DIR" -a -n "$INSTALL_VER" ||
+ fail "Configuration file ${CONFIG} not complete"
+ lib_dir="${INSTALL_DIR}/other"
+ test -x "${lib_dir}" ||
+ fail "Invalid Guest Additions configuration found."
+ # By default we want to configure X
+ dox11config="true"
+ # By default, we want to run our xorg.conf setup script
+ setupxorgconf="true"
+ # All but the oldest supported X servers can automatically set up the
+ # keyboard driver.
+ autokeyboard="--autoKeyboard"
+ # On more recent servers our kernel mouse driver will be used
+ # automatically
+ automouse="--autoMouse"
+ # We need to tell our xorg.conf hacking script whether /dev/psaux exists
+ nopsaux="--nopsaux"
+ case "`uname -r`" in 2.4.*)
+ test -c /dev/psaux && nopsaux="";;
+ esac
+ # Should we use the VMSVGA driver instead of VBoxVideo?
+ if grep 80eebeef /proc/bus/pci/devices > /dev/null; then
+ vmsvga=""
+ elif grep 15ad0405 /proc/bus/pci/devices > /dev/null; then
+ vmsvga="--vmsvga"
+ else
+ dox11config=""
+ fi
+ # The video driver to install for X.Org 6.9+
+ vboxvideo_src=
+ # The mouse driver to install for X.Org 6.9+
+ vboxmouse_src=
+ # The driver extension
+ driver_ext=".so"
+ # The configuration file we generate if no original was found but we need
+ # one.
+ main_cfg="/etc/X11/xorg.conf"
+
+ modules_dir=`X -showDefaultModulePath 2>&1` || modules_dir=
+ if [ -z "$modules_dir" ]; then
+ for dir in /usr/lib64/xorg/modules /usr/lib/xorg/modules /usr/X11R6/lib64/modules /usr/X11R6/lib/modules /usr/X11R6/lib/X11/modules; do
+ if [ -d $dir ]; then
+ modules_dir=$dir
+ break
+ fi
+ done
+ fi
+
+ case "${x_version}" in
+ 4.* | 6.* | 7.* | 1.?.* | 1.1[0-6].* )
+ blacklist_vboxvideo="yes"
+ ;;
+ esac
+ case "$oracle_release" in
+ Oracle*release\ 6.* )
+ # relevant for OL6/UEK4 but cannot hurt for other kernels
+ blacklist_vboxvideo="yes"
+ esac
+ if test -n "${blacklist_vboxvideo}"; then
+ test -d /etc/modprobe.d &&
+ echo "blacklist vboxvideo" > /etc/modprobe.d/blacklist-vboxvideo.conf
+ else
+ test -f /etc/modprobe.d/blacklist-vboxvideo.conf &&
+ rm -f /etc/modprobe.d/blacklist-vboxvideo.conf
+ # We do not want to load the driver if X.Org Server is already
+ # running, as without a driver the server will touch the hardware
+ # directly, causing problems.
+ ps -Af | grep -q '[X]org' || ${MODPROBE} -q vboxvideo
+ fi
+
+ test -z "$x_version" -o -z "$modules_dir" &&
+ {
+ echo "Could not find the X.Org or XFree86 Window System, skipping." >&2
+ exit 0
+ }
+
+ # openSUSE 10.3 shipped X.Org 7.2 with X.Org Server 1.3, but didn't
+ # advertise the fact.
+ if grep -q '10\.3' /etc/SuSE-release 2>/dev/null; then
+ case $x_version in 7.2.*)
+ x_version=1.3.0;;
+ esac
+ fi
+ case $x_version in
+ 1.*.99.* )
+ echo "Warning: unsupported pre-release version of X.Org Server installed. Not installing the X.Org drivers." >&2
+ dox11config=""
+ ;;
+ 1.11.* )
+ xserver_version="X.Org Server 1.11"
+ vboxvideo_src=vboxvideo_drv_111.so
+ test "$system" = "redhat" && test -z "${vmsvga}" || setupxorgconf=""
+ ;;
+ 1.10.* )
+ xserver_version="X.Org Server 1.10"
+ vboxvideo_src=vboxvideo_drv_110.so
+ test "$system" = "redhat" && test -z "${vmsvga}" || setupxorgconf=""
+ ;;
+ 1.9.* )
+ xserver_version="X.Org Server 1.9"
+ vboxvideo_src=vboxvideo_drv_19.so
+ # Fedora 14 to 16 patched out vboxvideo detection
+ test "$system" = "redhat" && test -z "${vmsvga}" || setupxorgconf=""
+ ;;
+ 1.8.* )
+ xserver_version="X.Org Server 1.8"
+ vboxvideo_src=vboxvideo_drv_18.so
+ # Fedora 13 shipped without vboxvideo detection
+ test "$system" = "redhat" && test -z "${vmsvga}" || setupxorgconf=""
+ ;;
+ 1.7.* )
+ xserver_version="X.Org Server 1.7"
+ vboxvideo_src=vboxvideo_drv_17.so
+ setupxorgconf=""
+ ;;
+ 1.6.* )
+ xserver_version="X.Org Server 1.6"
+ vboxvideo_src=vboxvideo_drv_16.so
+ vboxmouse_src=vboxmouse_drv_16.so
+ # SUSE SLE* with X.Org 1.6 does not do input autodetection;
+ # openSUSE does.
+ if grep -q -E '^SLE[^ ]' /etc/SuSE-brand 2>/dev/null; then
+ automouse=""
+ else
+ test "$system" = "suse" && setupxorgconf=""
+ fi
+ ;;
+ 1.5.* )
+ xserver_version="X.Org Server 1.5"
+ vboxvideo_src=vboxvideo_drv_15.so
+ vboxmouse_src=vboxmouse_drv_15.so
+ # Historical note: SUSE with X.Org Server 1.5 disabled automatic
+ # mouse configuration and was handled specially. However since our
+ # kernel driver seems to have problems with X.Org Server 1.5 anyway
+ # we just create an X.Org configuration file and use the user space
+ # one generally, no longer just for SUSE.
+ automouse=""
+ ;;
+ 1.4.* )
+ xserver_version="X.Org Server 1.4"
+ vboxvideo_src=vboxvideo_drv_14.so
+ vboxmouse_src=vboxmouse_drv_14.so
+ automouse=""
+ ;;
+ 1.3.* )
+ # This was the first release which gave the server version number
+ # rather than the X11 release version when you did 'X -version'.
+ xserver_version="X.Org Server 1.3"
+ vboxvideo_src=vboxvideo_drv_13.so
+ vboxmouse_src=vboxmouse_drv_13.so
+ automouse=""
+ ;;
+ 7.1.* | 7.2.* )
+ xserver_version="X.Org 7.1"
+ vboxvideo_src=vboxvideo_drv_71.so
+ vboxmouse_src=vboxmouse_drv_71.so
+ automouse=""
+ ;;
+ 6.9.* | 7.0.* )
+ xserver_version="X.Org 6.9/7.0"
+ vboxvideo_src=vboxvideo_drv_70.so
+ vboxmouse_src=vboxmouse_drv_70.so
+ automouse=""
+ ;;
+ 6.7* | 6.8.* | 4.2.* | 4.3.* )
+ # As the module binaries are the same we use one text for these
+ # four server versions.
+ xserver_version="XFree86 4.2/4.3 and X.Org 6.7/6.8"
+ driver_ext=.o
+ vboxvideo_src=vboxvideo_drv.o
+ vboxmouse_src=vboxmouse_drv.o
+ automouse=""
+ autokeyboard=""
+ case $x_version in
+ 6.8.* )
+ autokeyboard="--autoKeyboard"
+ ;;
+ 4.2.* | 4.3.* )
+ main_cfg="/etc/X11/XF86Config"
+ ;;
+ esac
+ ;;
+ 1.12.* | 1.13.* | 1.14.* | 1.15.* | 1.16.* | 1.17.* | 1.18.* )
+ xserver_version="X.Org Server ${x_version_short}"
+ vboxvideo_src=vboxvideo_drv_`echo ${x_version_short} | sed 's/\.//'`.so
+ setupxorgconf=""
+ test -f "${lib_dir}/${vboxvideo_src}" ||
+ {
+ echo "Warning: unknown version of the X Window System installed. Not installing X Window System drivers." >&2
+ dox11config=""
+ vboxvideo_src=""
+ }
+ ;;
+ * )
+ # For anything else, assume kernel drivers.
+ dox11config=""
+ ;;
+ esac
+ test -n "${dox11config}" &&
+ echo "Installing $xserver_version modules" >&2
+ case "$vboxvideo_src" in
+ ?*)
+ ln -s "${lib_dir}/$vboxvideo_src" "$modules_dir/drivers/vboxvideo_drv$driver_ext.new" &&
+ mv "$modules_dir/drivers/vboxvideo_drv$driver_ext.new" "$modules_dir/drivers/vboxvideo_drv$driver_ext";;
+ *)
+ rm "$modules_dir/drivers/vboxvideo_drv$driver_ext" 2>/dev/null
+ esac
+ case "$vboxmouse_src" in
+ ?*)
+ ln -s "${lib_dir}/$vboxmouse_src" "$modules_dir/input/vboxmouse_drv$driver_ext.new" &&
+ mv "$modules_dir/input/vboxmouse_drv$driver_ext.new" "$modules_dir/input/vboxmouse_drv$driver_ext";;
+ *)
+ rm "$modules_dir/input/vboxmouse_drv$driver_ext" 2>/dev/null
+ esac
+
+ if test -n "$dox11config"; then
+ # Certain Ubuntu/Debian versions use a special PCI-id file to identify
+ # video drivers. Some versions have the directory and don't use it.
+ # Those versions can autoload vboxvideo though, so we don't need to
+ # hack the configuration file for them.
+ test "$system" = "debian" -a -d /usr/share/xserver-xorg/pci &&
+ {
+ rm -f "/usr/share/xserver-xorg/pci/vboxvideo.ids"
+ ln -s "${lib_dir}/vboxvideo.ids" /usr/share/xserver-xorg/pci 2>/dev/null
+ test -n "$automouse" && setupxorgconf=""
+ }
+
+ # Do the XF86Config/xorg.conf hack for those versions that require it
+ configured=""
+ generated=""
+ if test -n "$setupxorgconf"; then
+ for i in $x11conf_files; do
+ if test -r "$i"; then
+ if grep -q "VirtualBox generated" "$i"; then
+ generated="$generated `printf "$i\n"`"
+ else
+ "${lib_dir}/x11config.sh" $autokeyboard $automouse $nopsaux $vmsvga "$i"
+ fi
+ configured="true"
+ fi
+ # Timestamp, so that we can see if the config file is changed
+ # by someone else later
+ test -r "$i.vbox" && touch "$i.vbox"
+ done
+ # X.Org Server 1.5 and 1.6 can detect hardware they know, but they
+ # need a configuration file for VBoxVideo.
+ nobak_cfg="`expr "${main_cfg}" : '\([^.]*\)'`.vbox.nobak"
+ if test -z "$configured"; then
+ touch "$main_cfg"
+ "${lib_dir}/x11config.sh" $autokeyboard $automouse $nopsaux $vmsvga --noBak "$main_cfg"
+ touch "${nobak_cfg}"
+ fi
+ fi
+ test -n "$generated" &&
+ cat >&2 << EOF
+The following X.Org/XFree86 configuration files were originally generated by
+the VirtualBox Guest Additions and were not modified:
+
+$generated
+
+EOF
+ tty >/dev/null && cat << EOF
+You may need to restart the Window System (or just restart the guest system)
+to enable the Guest Additions.
+
+EOF
+ fi
+
+ case "$redhat_release" in
+ # Install selinux policy for Fedora 7 and 8 to allow the X server to
+ # open device files
+ Fedora\ release\ 7* | Fedora\ release\ 8* )
+ semodule -i "${lib_dir}/vbox_x11.pp" > /dev/null 2>&1
+ ;;
+ # Similar for the accelerated graphics check on Fedora 15
+ Fedora\ release\ 15* )
+ semodule -i "${lib_dir}/vbox_accel.pp" > /dev/null 2>&1
+ ;;
+ esac
+
+ # Install selinux policy for Fedora 8 to allow the X server to
+ # open our drivers
+ case "$redhat_release" in
+ Fedora\ release\ 8* )
+ chcon -u system_u -t lib_t "${lib_dir}"/*.so
+ ;;
+ esac
+
+ # Our logging code generates some glue code on 32-bit systems. At least F10
+ # needs a rule to allow this. Send all output to /dev/null in case this is
+ # completely irrelevant on the target system.
+ chcon -t unconfined_execmem_exec_t '/usr/bin/VBoxClient' > /dev/null 2>&1
+ semanage fcontext -a -t unconfined_execmem_exec_t '/usr/bin/VBoxClient' > /dev/null 2>&1
+
+ # And set up VBoxClient to start when the X session does
+ install_x11_startup_app "${lib_dir}/98vboxadd-xclient" "${lib_dir}/vboxclient.desktop" VBoxClient VBoxClient-all ||
+ fail "Failed to set up VBoxClient to start automatically."
+ ln -s "${lib_dir}/98vboxadd-xclient" /usr/bin/VBoxClient-all 2>/dev/null
+ case "${x_version}" in 4.* | 6.* | 7.* | 1.?.* | 1.1* )
+ setup_opengl
+ esac
+ # Try enabling VMSVGA drm device resizing.
+ VBoxClient --vmsvga
+}
+
+cleanup()
+{
+ # Restore xorg.conf files as far as possible
+ # List of generated files which have been changed since we generated them
+ newer=""
+ # Are we dealing with a legacy information which didn't support
+ # uninstallation?
+ legacy=""
+ # Do any of the restored configuration files still reference our drivers?
+ failed=""
+ # Have we encountered a "nobak" configuration file which means that there
+ # is no original file to restore?
+ nobak=""
+ test -r "$CONFIG_DIR/$CONFIG" || legacy="true"
+ for main_cfg in "/etc/X11/xorg.conf" "/etc/X11/XF86Config"; do
+ nobak_cfg="`expr "${main_cfg}" : '\([^.]*\)'`.vbox.nobak"
+ if test -r "${nobak_cfg}"; then
+ test -r "${main_cfg}" &&
+ if test -n "${legacy}" -o ! "${nobak_cfg}" -ot "${main_cfg}"; then
+ rm -f "${nobak_cfg}" "${main_cfg}"
+ else
+ newer="${newer}`printf " ${main_cfg} (no original)\n"`"
+ fi
+ nobak="true"
+ fi
+ done
+ if test -z "${nobak}"; then
+ for i in $x11conf_files; do
+ if test -r "$i.vbox"; then
+ if test ! "$i" -nt "$i.vbox" -o -n "$legacy"; then
+ mv -f "$i.vbox" "$i"
+ grep -q -E 'vboxvideo|vboxmouse' "$i" &&
+ failed="$failed`printf " $i\n"`"
+ else
+ newer="$newer`printf " $i ($i.vbox)\n"`"
+ fi
+ fi
+ done
+ fi
+ test -n "$newer" && cat >&2 << EOF
+
+The following X.Org/XFree86 configuration files were not restored, as they may
+have been changed since they were generated by the VirtualBox Guest Additions.
+You may wish to restore these manually. The file name in brackets is the
+original version.
+
+$newer
+
+EOF
+ test -n "$failed" && cat >&2 << EOF
+
+The following X.Org/XFree86 configuration files were restored, but still
+contain references to the Guest Additions drivers. You may wish to check and
+possibly correct the restored configuration files to be sure that the server
+will continue to work after it is restarted.
+
+$failed
+
+EOF
+
+ # Remove X.Org drivers
+ modules_dir=`X -showDefaultModulePath 2>&1` || modules_dir=
+ if [ -z "$modules_dir" ]; then
+ for dir in /usr/lib64/xorg/modules /usr/lib/xorg/modules /usr/X11R6/lib64/modules /usr/X11R6/lib/modules /usr/X11R6/lib/X11/modules; do
+ if [ -d $dir ]; then
+ modules_dir=$dir
+ break
+ fi
+ done
+ fi
+ rm -f "$modules_dir/drivers/vboxvideo_drv"* 2>/dev/null
+ rm -f "$modules_dir/input/vboxmouse_drv"* 2>/dev/null
+
+ # Remove the link to vboxvideo_dri.so
+ for dir in /usr/lib/dri /usr/lib32/dri /usr/lib64/dri \
+ /usr/lib/xorg/modules/dri /usr/lib32/xorg/modules/dri \
+ /usr/lib64/xorg/modules/dri /usr/lib/i386-linux-gnu/dri \
+ /usr/lib/x86_64-linux-gnu/dri; do
+ if [ -d $dir ]; then
+ rm -f "$dir/vboxvideo_dri.so" 2>/dev/null
+ fi
+ done
+
+ # Remove VBoxClient autostart files
+ rm /etc/X11/Xsession.d/98vboxadd-xclient 2>/dev/null
+ rm /etc/X11/xinit.d/98vboxadd-xclient 2>/dev/null
+ rm /etc/X11/xinit/xinitrc.d/98vboxadd-xclient.sh 2>/dev/null
+ rm /etc/xdg/autostart/vboxclient.desktop 2>/dev/null
+ rm /usr/share/autostart/vboxclient.desktop 2>/dev/null
+ rm /usr/bin/VBoxClient-all 2>/dev/null
+
+ # Remove other files
+ rm /usr/share/xserver-xorg/pci/vboxvideo.ids 2>/dev/null
+ return 0
+}
+
+dmnstatus()
+{
+ /bin/true
+}
+
+case "$1" in
+start)
+ start
+ ;;
+stop)
+ stop
+ ;;
+restart)
+ restart
+ ;;
+setup)
+ setup
+ ;;
+cleanup)
+ cleanup
+ ;;
+status)
+ dmnstatus
+ ;;
+*)
+ echo "Usage: $0 {start|stop|restart|status}"
+ exit 1
+esac
+
+exit
diff --git a/src/VBox/Additions/linux/installer/vboxadd.sh b/src/VBox/Additions/linux/installer/vboxadd.sh
new file mode 100755
index 00000000..5eae0cb1
--- /dev/null
+++ b/src/VBox/Additions/linux/installer/vboxadd.sh
@@ -0,0 +1,595 @@
+#! /bin/sh
+# $Id: vboxadd.sh $
+## @file
+# Linux Additions kernel module init script ($Revision: 128393 $)
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+# X-Start-Before is a Debian Addition which we use when converting to
+# a systemd unit. X-Service-Type is our own invention, also for systemd.
+
+# chkconfig: 345 10 90
+# description: VirtualBox Linux Additions kernel modules
+#
+### BEGIN INIT INFO
+# Provides: vboxadd
+# Required-Start:
+# Required-Stop:
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# X-Start-Before: display-manager
+# X-Service-Type: oneshot
+# Description: VirtualBox Linux Additions kernel modules
+### END INIT INFO
+
+## @todo This file duplicates a lot of script with vboxdrv.sh. When making
+# changes please try to reduce differences between the two wherever possible.
+
+# Testing:
+# * Should fail if the configuration file is missing or missing INSTALL_DIR or
+# INSTALL_VER entries.
+# * vboxadd user and vboxsf groups should be created if they do not exist - test
+# by removing them before installing.
+# * Shared folders can be mounted and auto-mounts accessible to vboxsf group,
+# including on recent Fedoras with SELinux.
+# * Setting INSTALL_NO_MODULE_BUILDS inhibits modules and module automatic
+# rebuild script creation; otherwise modules, user, group, rebuild script,
+# udev rule and shared folder mount helper should be created/set up.
+# * Setting INSTALL_NO_MODULE_BUILDS inhibits module load and unload on start
+# and stop.
+# * Uninstalling the Additions and re-installing them does not trigger warnings.
+
+export LC_ALL=C
+PATH=$PATH:/bin:/sbin:/usr/sbin
+PACKAGE=VBoxGuestAdditions
+MODPROBE=/sbin/modprobe
+OLDMODULES="vboxguest vboxadd vboxsf vboxvfs vboxvideo"
+SERVICE="VirtualBox Guest Additions"
+QUICKSETUP=
+## systemd logs information about service status, otherwise do that ourselves.
+QUIET=
+test -z "${TARGET_VER}" && TARGET_VER=`uname -r`
+# Marker to ignore a particular kernel version which was already installed.
+SKIPFILE_BASE=/var/lib/VBoxGuestAdditions/skip
+
+setup_log()
+{
+ test -z "${LOG}" || return 0
+ # Rotate log files
+ LOG="/var/log/vboxadd-setup.log"
+ mv "${LOG}.3" "${LOG}.4" 2>/dev/null
+ mv "${LOG}.2" "${LOG}.3" 2>/dev/null
+ mv "${LOG}.1" "${LOG}.2" 2>/dev/null
+ mv "${LOG}" "${LOG}.1" 2>/dev/null
+}
+
+if $MODPROBE -c 2>/dev/null | grep -q '^allow_unsupported_modules *0'; then
+ MODPROBE="$MODPROBE --allow-unsupported-modules"
+fi
+
+# Check architecture
+cpu=`uname -m`;
+case "$cpu" in
+ i[3456789]86|x86)
+ cpu="x86"
+ ldconfig_arch="(libc6)"
+ lib_candidates="/usr/lib/i386-linux-gnu /usr/lib /lib"
+ ;;
+ x86_64|amd64)
+ cpu="amd64"
+ ldconfig_arch="(libc6,x86-64)"
+ lib_candidates="/usr/lib/x86_64-linux-gnu /usr/lib64 /usr/lib /lib64 /lib"
+ ;;
+esac
+for i in $lib_candidates; do
+ if test -d "$i/VBoxGuestAdditions"; then
+ lib_path=$i
+ break
+ fi
+done
+
+# Preamble for Gentoo
+if [ "`which $0`" = "/sbin/rc" ]; then
+ shift
+fi
+
+begin()
+{
+ test -n "${QUIET}" || echo "${SERVICE}: ${1}"
+}
+
+info()
+{
+ if test -z "${QUIET}"; then
+ echo "${SERVICE}: $1" | fold -s
+ else
+ echo "$1" | fold -s
+ fi
+}
+
+fail()
+{
+ log "${1}"
+ echo "$1" >&2
+ echo "The log file $LOG may contain further information." >&2
+ exit 1
+}
+
+log()
+{
+ setup_log
+ echo "${1}" >> "${LOG}"
+}
+
+module_build_log()
+{
+ log "Error building the module. Build output follows."
+ echo ""
+ echo "${1}" >> "${LOG}"
+}
+
+dev=/dev/vboxguest
+userdev=/dev/vboxuser
+config=/var/lib/VBoxGuestAdditions/config
+owner=vboxadd
+group=1
+
+if test -r $config; then
+ . $config
+else
+ fail "Configuration file $config not found"
+fi
+test -n "$INSTALL_DIR" -a -n "$INSTALL_VER" ||
+ fail "Configuration file $config not complete"
+
+running_vboxguest()
+{
+ lsmod | grep -q "vboxguest[^_-]"
+}
+
+running_vboxadd()
+{
+ lsmod | grep -q "vboxadd[^_-]"
+}
+
+running_vboxsf()
+{
+ lsmod | grep -q "vboxsf[^_-]"
+}
+
+running_vboxvideo()
+{
+ lsmod | grep -q "vboxvideo[^_-]"
+}
+
+do_vboxguest_non_udev()
+{
+ if [ ! -c $dev ]; then
+ maj=`sed -n 's;\([0-9]\+\) vboxguest;\1;p' /proc/devices`
+ if [ ! -z "$maj" ]; then
+ min=0
+ else
+ min=`sed -n 's;\([0-9]\+\) vboxguest;\1;p' /proc/misc`
+ if [ ! -z "$min" ]; then
+ maj=10
+ fi
+ fi
+ test -n "$maj" || {
+ rmmod vboxguest 2>/dev/null
+ fail "Cannot locate the VirtualBox device"
+ }
+
+ mknod -m 0664 $dev c $maj $min || {
+ rmmod vboxguest 2>/dev/null
+ fail "Cannot create device $dev with major $maj and minor $min"
+ }
+ fi
+ chown $owner:$group $dev 2>/dev/null || {
+ rm -f $dev 2>/dev/null
+ rm -f $userdev 2>/dev/null
+ rmmod vboxguest 2>/dev/null
+ fail "Cannot change owner $owner:$group for device $dev"
+ }
+
+ if [ ! -c $userdev ]; then
+ maj=10
+ min=`sed -n 's;\([0-9]\+\) vboxuser;\1;p' /proc/misc`
+ if [ ! -z "$min" ]; then
+ mknod -m 0666 $userdev c $maj $min || {
+ rm -f $dev 2>/dev/null
+ rmmod vboxguest 2>/dev/null
+ fail "Cannot create device $userdev with major $maj and minor $min"
+ }
+ chown $owner:$group $userdev 2>/dev/null || {
+ rm -f $dev 2>/dev/null
+ rm -f $userdev 2>/dev/null
+ rmmod vboxguest 2>/dev/null
+ fail "Cannot change owner $owner:$group for device $userdev"
+ }
+ fi
+ fi
+}
+
+start()
+{
+ begin "Starting."
+ if test -z "${INSTALL_NO_MODULE_BUILDS}"; then
+ setup --quick
+ test -d /sys &&
+ ps -A -o comm | grep -q '/*udevd$' 2>/dev/null ||
+ no_udev=1
+ running_vboxguest || {
+ rm -f $dev || {
+ fail "Cannot remove $dev"
+ }
+ rm -f $userdev || {
+ fail "Cannot remove $userdev"
+ }
+ $MODPROBE vboxguest >/dev/null 2>&1 ||
+ fail "modprobe vboxguest failed"
+ case "$no_udev" in 1)
+ sleep .5;;
+ esac
+ }
+ case "$no_udev" in 1)
+ do_vboxguest_non_udev;;
+ esac
+
+ running_vboxsf || {
+ $MODPROBE vboxsf > /dev/null 2>&1 ||
+ info "modprobe vboxsf failed"
+ }
+ fi # INSTALL_NO_MODULE_BUILDS
+
+ # Put the X.Org driver in place. This is harmless if it is not needed.
+ # Also set up the OpenGL library.
+ myerr=`"${INSTALL_DIR}/init/vboxadd-x11" setup 2>&1`
+ test -z "${myerr}" || log "${myerr}"
+
+ # Mount all shared folders from /etc/fstab. Normally this is done by some
+ # other startup script but this requires the vboxdrv kernel module loaded.
+ # This isn't necessary anymore as the vboxsf module is autoloaded.
+ # mount -a -t vboxsf
+
+ return 0
+}
+
+stop()
+{
+ begin "Stopping."
+ test -n "${INSTALL_NO_MODULE_BUILDS}" || setup --quick
+ if test -r /etc/ld.so.conf.d/00vboxvideo.conf; then
+ rm /etc/ld.so.conf.d/00vboxvideo.conf
+ ldconfig
+ fi
+ if ! umount -a -t vboxsf 2>/dev/null; then
+ fail "Cannot unmount vboxsf folders"
+ fi
+ test -n "${INSTALL_NO_MODULE_BUILDS}" ||
+ info "You may need to restart your guest system to finish removing the guest drivers."
+ return 0
+}
+
+restart()
+{
+ stop && start
+ return 0
+}
+
+## Update the initramfs. Debian and Ubuntu put the graphics driver in, and
+# need the touch(1) command below. Everyone else that I checked just need
+# the right module alias file from depmod(1) and only use the initramfs to
+# load the root filesystem, not the boot splash. update-initramfs works
+# for the first two and dracut for every one else I checked. We are only
+# interested in distributions recent enough to use the KMS vboxvideo driver.
+update_initramfs()
+{
+ ## kernel version to update for.
+ version="${1}"
+ depmod "${version}"
+ rm -f "/lib/modules/${version}/initrd/vboxvideo"
+ test ! -d "/lib/modules/${version}/initrd" ||
+ test ! -f "/lib/modules/${version}/misc/vboxvideo.ko" ||
+ touch "/lib/modules/${version}/initrd/vboxvideo"
+
+ # Systems without systemd-inhibit probably don't need their initramfs
+ # rebuild here anyway.
+ type systemd-inhibit >/dev/null 2>&1 || return
+ if type dracut >/dev/null 2>&1; then
+ systemd-inhibit --why="Installing VirtualBox Guest Additions" \
+ dracut -f --kver "${version}"
+ elif type update-initramfs >/dev/null 2>&1; then
+ systemd-inhibit --why="Installing VirtualBox Guest Additions" \
+ update-initramfs -u -k "${version}"
+ fi
+}
+
+# Remove any existing VirtualBox guest kernel modules from the disk, but not
+# from the kernel as they may still be in use
+cleanup_modules()
+{
+ # Needed for Ubuntu and Debian, see update_initramfs
+ rm -f /lib/modules/*/initrd/vboxvideo
+ for i in /lib/modules/*/misc; do
+ KERN_VER="${i%/misc}"
+ KERN_VER="${KERN_VER#/lib/modules/}"
+ unset do_update
+ for j in ${OLDMODULES}; do
+ test -f "${i}/${j}.ko" && do_update=1 && rm -f "${i}/${j}.ko"
+ done
+ test -z "$do_update" || update_initramfs "$KERN_VER"
+ # Remove empty /lib/modules folders which may have been kept around
+ rmdir -p "${i}" 2>/dev/null || true
+ unset keep
+ for j in /lib/modules/"${KERN_VER}"/*; do
+ name="${j##*/}"
+ test -d "${name}" || test "${name%%.*}" != modules && keep=1
+ done
+ if test -z "${keep}"; then
+ rm -rf /lib/modules/"${KERN_VER}"
+ rm -f /boot/initrd.img-"${KERN_VER}"
+ fi
+ done
+ for i in ${OLDMODULES}; do
+ # We no longer support DKMS, remove any leftovers.
+ rm -rf "/var/lib/dkms/${i}"*
+ done
+ rm -f /etc/depmod.d/vboxvideo-upstream.conf
+ rm -f "$SKIPFILE_BASE"-*
+}
+
+# Build and install the VirtualBox guest kernel modules
+setup_modules()
+{
+ KERN_VER="$1"
+ test -n "$KERN_VER" || return 1
+ test ! -f /lib/modules/"$KERN_VER"/misc/vboxguest.ko || return 0
+ test ! -f /lib/modules/"$KERN_VER"/misc/vboxguest.o || return 0
+ test -d /lib/modules/"$KERN_VER"/build || return 0
+ test ! -f "$SKIPFILE_BASE"-"$KERN_VER" || return 0
+ export KERN_VER
+ info "Building the modules for kernel $KERN_VER."
+
+ log "Building the main Guest Additions module for kernel $KERN_VER."
+ if ! myerr=`$BUILDINTMP \
+ --save-module-symvers /tmp/vboxguest-Module.symvers \
+ --module-source $MODULE_SRC/vboxguest \
+ --no-print-directory install 2>&1`; then
+ # If check_module_dependencies.sh fails it prints a message itself.
+ module_build_log "$myerr"
+ "${INSTALL_DIR}"/other/check_module_dependencies.sh 2>&1 &&
+ info "Look at $LOG to find out what went wrong"
+ return 0
+ fi
+ log "Building the shared folder support module."
+ if ! myerr=`$BUILDINTMP \
+ --use-module-symvers /tmp/vboxguest-Module.symvers \
+ --module-source $MODULE_SRC/vboxsf \
+ --no-print-directory install 2>&1`; then
+ module_build_log "$myerr"
+ info "Look at $LOG to find out what went wrong"
+ return 0
+ fi
+ log "Building the graphics driver module."
+ if ! myerr=`$BUILDINTMP \
+ --use-module-symvers /tmp/vboxguest-Module.symvers \
+ --module-source $MODULE_SRC/vboxvideo \
+ --no-print-directory install 2>&1`; then
+ module_build_log "$myerr"
+ info "Look at $LOG to find out what went wrong"
+ fi
+ [ -d /etc/depmod.d ] || mkdir /etc/depmod.d
+ echo "override vboxguest * misc" > /etc/depmod.d/vboxvideo-upstream.conf
+ echo "override vboxsf * misc" >> /etc/depmod.d/vboxvideo-upstream.conf
+ echo "override vboxvideo * misc" >> /etc/depmod.d/vboxvideo-upstream.conf
+ update_initramfs "${KERN_VER}"
+ return 0
+}
+
+create_vbox_user()
+{
+ # This is the LSB version of useradd and should work on recent
+ # distributions
+ useradd -d /var/run/vboxadd -g 1 -r -s /bin/false vboxadd >/dev/null 2>&1 || true
+ # And for the others, we choose a UID ourselves
+ useradd -d /var/run/vboxadd -g 1 -u 501 -o -s /bin/false vboxadd >/dev/null 2>&1 || true
+
+}
+
+create_udev_rule()
+{
+ # Create udev description file
+ if [ -d /etc/udev/rules.d ]; then
+ udev_call=""
+ udev_app=`which udevadm 2> /dev/null`
+ if [ $? -eq 0 ]; then
+ udev_call="${udev_app} version 2> /dev/null"
+ else
+ udev_app=`which udevinfo 2> /dev/null`
+ if [ $? -eq 0 ]; then
+ udev_call="${udev_app} -V 2> /dev/null"
+ fi
+ fi
+ udev_fix="="
+ if [ "${udev_call}" != "" ]; then
+ udev_out=`${udev_call}`
+ udev_ver=`expr "$udev_out" : '[^0-9]*\([0-9]*\)'`
+ if [ "$udev_ver" = "" -o "$udev_ver" -lt 55 ]; then
+ udev_fix=""
+ fi
+ fi
+ ## @todo 60-vboxadd.rules -> 60-vboxguest.rules ?
+ echo "KERNEL=${udev_fix}\"vboxguest\", NAME=\"vboxguest\", OWNER=\"vboxadd\", MODE=\"0660\"" > /etc/udev/rules.d/60-vboxadd.rules
+ echo "KERNEL=${udev_fix}\"vboxuser\", NAME=\"vboxuser\", OWNER=\"vboxadd\", MODE=\"0666\"" >> /etc/udev/rules.d/60-vboxadd.rules
+ fi
+}
+
+create_module_rebuild_script()
+{
+ # And a post-installation script for rebuilding modules when a new kernel
+ # is installed.
+ mkdir -p /etc/kernel/postinst.d /etc/kernel/prerm.d
+ cat << EOF > /etc/kernel/postinst.d/vboxadd
+#!/bin/sh
+# This only works correctly on Debian derivatives - Red Hat calls it before
+# installing the right header files.
+/sbin/rcvboxadd quicksetup "\${1}"
+exit 0
+EOF
+ cat << EOF > /etc/kernel/prerm.d/vboxadd
+#!/bin/sh
+for i in ${OLDMODULES}; do rm -f /lib/modules/"\${1}"/misc/"\${i}".ko; done
+rmdir -p /lib/modules/"\$1"/misc 2>/dev/null
+exit 0
+EOF
+ chmod 0755 /etc/kernel/postinst.d/vboxadd /etc/kernel/prerm.d/vboxadd
+}
+
+shared_folder_setup()
+{
+ # Add a group "vboxsf" for Shared Folders access
+ # All users which want to access the auto-mounted Shared Folders have to
+ # be added to this group.
+ groupadd -r -f vboxsf >/dev/null 2>&1
+
+ # Put the mount.vboxsf mount helper in the right place.
+ ## @todo It would be nicer if the kernel module just parsed parameters
+ # itself instead of needing a separate binary to do that.
+ ln -sf "${INSTALL_DIR}/other/mount.vboxsf" /sbin
+ # SELinux security context for the mount helper.
+ if test -e /etc/selinux/config; then
+ # This is correct. semanage maps this to the real path, and it aborts
+ # with an error, telling you what you should have typed, if you specify
+ # the real path. The "chcon" is there as a back-up for old guests.
+ command -v semanage > /dev/null &&
+ semanage fcontext -a -t mount_exec_t "${INSTALL_DIR}/other/mount.vboxsf"
+ chcon -t mount_exec_t "${INSTALL_DIR}/other/mount.vboxsf"
+ fi
+}
+
+# setup_script
+setup()
+{
+ export BUILD_TYPE
+ export USERNAME
+
+ test x"$1" != x--quick || QUICKSETUP=true
+ test -n "$QUICKSETUP" || cleanup
+ MODULE_SRC="$INSTALL_DIR/src/vboxguest-$INSTALL_VER"
+ BUILDINTMP="$MODULE_SRC/build_in_tmp"
+ test ! -e /etc/selinux/config ||
+ chcon -t bin_t "$BUILDINTMP"
+
+ if test -z "$INSTALL_NO_MODULE_BUILDS"; then
+ if test -z "$QUICKSETUP"; then
+ info "Building the VirtualBox Guest Additions kernel modules. This may take a while."
+ info "To build modules for other installed kernels, run"
+ info " /sbin/rcvboxadd quicksetup <version>"
+ for setupi in /lib/modules/*; do
+ KERN_VER="${setupi##*/}"
+ # For a full setup, mark kernels we do not want to build.
+ touch "$SKIPFILE_BASE"-"$KERN_VER"
+ done
+ fi
+ # That is, we mark all but the requested kernel.
+ rm -f "$SKIPFILE_BASE"-"$TARGET_VER"
+ test -d /lib/modules/"$TARGET_VER"/build || test -n "$QUICKSETUP" ||
+ info "Kernel headers not found for target kernel $TARGET_VER. \
+Please install them and execute
+ /sbin/rcvboxadd setup"
+ for setupi in /lib/modules/*; do
+ KERN_VER="${setupi##*/}"
+ setup_modules "$KERN_VER"
+ done
+ depmod
+ fi
+ create_vbox_user
+ create_udev_rule
+ test -n "${INSTALL_NO_MODULE_BUILDS}" || create_module_rebuild_script
+ test -z "$QUICKSETUP" || return 0
+ shared_folder_setup
+ if running_vboxguest || running_vboxadd; then
+ info "Running kernel modules will not be replaced until the system is restarted"
+ fi
+ return 0
+}
+
+# cleanup_script
+cleanup()
+{
+ if test -z "${INSTALL_NO_MODULE_BUILDS}"; then
+ # Delete old versions of VBox modules.
+ cleanup_modules
+ depmod
+
+ # Remove old module sources
+ for i in $OLDMODULES; do
+ rm -rf /usr/src/$i-*
+ done
+ fi
+
+ # Clean-up X11-related bits
+ "${INSTALL_DIR}/init/vboxadd-x11" cleanup
+
+ # Remove other files
+ if test -z "${INSTALL_NO_MODULE_BUILDS}"; then
+ rm -f /etc/kernel/postinst.d/vboxadd /etc/kernel/prerm.d/vboxadd
+ rmdir -p /etc/kernel/postinst.d /etc/kernel/prerm.d 2>/dev/null
+ fi
+ rm /sbin/mount.vboxsf 2>/dev/null
+ rm /etc/udev/rules.d/60-vboxadd.rules 2>/dev/null
+}
+
+dmnstatus()
+{
+ if running_vboxguest; then
+ echo "The VirtualBox Additions are currently running."
+ else
+ echo "The VirtualBox Additions are not currently running."
+ fi
+}
+
+case "$2" in quiet)
+ QUIET=yes;;
+esac
+case "$1" in
+start)
+ start
+ ;;
+stop)
+ stop
+ ;;
+restart)
+ restart
+ ;;
+setup)
+ setup
+ start
+ ;;
+quicksetup)
+ test -z "$2" || test ! -d /lib/modules/"$2"/build || TARGET_VER="$2"
+ setup --quick
+ ;;
+cleanup)
+ cleanup
+ ;;
+status)
+ dmnstatus
+ ;;
+*)
+ echo "Usage: $0 {start|stop|restart|status|setup|quicksetup|cleanup} [quiet]"
+ exit 1
+esac
+
+exit
diff --git a/src/VBox/Additions/linux/lightdm-greeter/.scm-settings b/src/VBox/Additions/linux/lightdm-greeter/.scm-settings
new file mode 100644
index 00000000..e425fa24
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/.scm-settings
@@ -0,0 +1,20 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for light-dm greeter.
+#
+
+#
+# Copyright (C) 2010-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+
+/liblightdm-gobject-*/*.c|/liblightdm-gobject-*/*.h: --external-copyright --no-strip-trailing-blanks --no-convert-tabs --lgpl-disclaimer --no-fix-header-guards
+
diff --git a/src/VBox/Additions/linux/lightdm-greeter/Config.kmk b/src/VBox/Additions/linux/lightdm-greeter/Config.kmk
new file mode 100644
index 00000000..c8373c94
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/Config.kmk
@@ -0,0 +1,31 @@
+# $Id: Config.kmk $
+## @file
+# kBuild Configuration file for the lightdm-greeter
+#
+
+#
+# Copyright (C) 2016-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+
+VBOX_LIGHTDM_GREETER_CONFIG_KMK_INCLUDED = 1
+
+# Include the top-level configure file.
+ifndef VBOX_ROOT_CONFIG_KMK_INCLUDED
+ include $(PATH_ROOT)/Config.kmk
+endif
+
+SDK_VBoxGlib20WithIo = glib-2.0 and gio-2.0
+SDK_VBoxGlib20WithIo_VBOX_PKG_CONFIG_CFLAGS := $(shell pkg-config gio-2.0 glib-2.0 --cflags)
+SDK_VBoxGlib20WithIo_INCS = $(patsubst -I%,%,$(filter -I%,$(SDK_VBoxGlib20WithIo_VBOX_PKG_CONFIG_CFLAGS)))
+SDK_VBoxGlib20WithIo_CFLAGS = $(filter-out -I%,$(SDK_VBoxGlib20WithIo_VBOX_PKG_CONFIG_CFLAGS))
+SDK_VBoxGlib20WithIo_LDFLAGS := $(shell pkg-config gio-2.0 glib-2.0 --libs)
+
diff --git a/src/VBox/Additions/linux/lightdm-greeter/Makefile.kmk b/src/VBox/Additions/linux/lightdm-greeter/Makefile.kmk
new file mode 100644
index 00000000..5cc838c6
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/Makefile.kmk
@@ -0,0 +1,104 @@
+# $Id: Makefile.kmk $
+## @file
+# Makefile for VBox LightDM greeter for providing automated logons.
+#
+
+#
+# Copyright (C) 2012-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+ifndef VBOX_LIGHTDM_GREETER_CONFIG_KMK_INCLUDED
+ include $(PATH_SUB_CURRENT)/Config.kmk
+endif
+
+ifndef VBOX_OSE
+ include $(PATH_SUB_CURRENT)/liblightdm-gobject-1.5.0/Makefile.kmk
+endif
+
+# Enable building with FLTK UI + PNG support.
+VBOX_WITH_FLTK := 1
+VBOX_GREETER_WITH_PNG_SUPPORT := 1
+
+# The greeter module.
+PROGRAMS += vbox-greeter
+vbox-greeter_TEMPLATE = VBOXGUESTR3EXE
+vbox-greeter_SDKS = VBoxGlib20WithIo
+vbox-greeter_DEFS = LOG_TO_BACKDOOR VBOX_WITH_HGCM
+vbox-greeter_DEFS += \
+ VBOX_BUILD_TARGET=\"$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH)\"
+vbox-greeter_DEFS += \
+ $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \
+ $(if $(VBOX_GREETER_WITH_PNG_SUPPORT),VBOX_GREETER_WITH_PNG_SUPPORT,)
+ifdef VBOX_WITH_FLTK
+ vbox-greeter_DEFS += \
+ VBOX_WITH_FLTK
+else
+ vbox-greeter_DEFS += \
+ GTK_DISABLE_SINGLE_INCLUDES \
+ GDK_DISABLE_DEPRECATED
+endif
+vbox-greeter_CFLAGS := $(if $(VBOX_OSE),%(filter-out -I%,$(shell pkg-config --cflags liblightdm-gobject-1)),)
+## @todo r=bird: Why are we cooking our own lightdm-gobject-1 but using system headers?
+## That sounds like a very risky business to me. I've added liblightdm-gobject-1.5.0
+## to the INCS, however lightdm.h is missing and will be taken from the system.
+vbox-greeter_INCS := \
+ /usr/lib/i386-linux-gnu/glib-2.0/include \
+ /usr/lib/x86_64-linux-gnu/glib-2.0/include \
+ /usr/include/glib-2.0 \
+ $(if $(VBOX_OSE),,liblightdm-gobject-1.5.0) \
+ /usr/include/lightdm-gobject-1 \
+ $(if $(VBOX_OSE),$(patsubst -I%,%,%(filter -I%,$(shell pkg-config --cflags liblightdm-gobject-1))),)
+ifndef VBOX_WITH_FLTK
+ vbox-greeter_INCS += \
+ /usr/include/glib-2.0 \
+ /usr/include/gtk-3.0 \
+ /usr/include/pango-1.0 \
+ /usr/include/cairo \
+ /usr/include/gdk-pixbuf-2.0 \
+ /usr/include/atk-1.0
+endif
+
+vbox-greeter_SOURCES = \
+ vbox-greeter.cpp
+
+vbox-greeter_LIBS := \
+ $(if $(VBOX_OSE),lightdm-gobject-1,$(VBOX_PATH_ADDITIONS_LIB)/VBox-liblightdm-gobject$(VBOX_SUFF_LIB)) \
+ glib-2.0 \
+ gio-2.0 \
+ gobject-2.0 \
+ $(VBOX_LIB_IPRT_GUEST_R3_SHARED) \
+ $(VBOX_LIB_VBGL_R3_SHARED) \
+ $(VBOX_LIB_IPRT_GUEST_R3_SHARED)
+ifdef VBOX_WITH_FLTK
+ vbox-greeter_LIBS += fltk
+ ifdef VBOX_GREETER_WITH_PNG_SUPPORT
+ vbox-greeter_LIBS += fltk_images
+ endif
+ if $(HOSTNAME) == "3960x.dev" && $(USER) == "bird" # whatever.
+ vbox-greeter_LIBS += stdc++
+ endif
+else
+ vbox-greeter_LIBS += gtk-3
+endif
+ifeq ($(KBUILD_TARGET),linux)
+vbox-greeter_LIBS += crypt
+endif
+
+vbox-greeter_LDFLAGS = $(if $(VBOX_OSE),$(shell pkg-config --libs liblightdm-gobject-1),)
+ifdef VBOX_WITH_FLTK
+ #vbox-greeter_LDFLAGS = -Wl,-Bsymbolic-functions -Wl,-z,relro /usr/lib/i386-linux-gnu/libfltk.a -lXext -lXft -lfontconfig -lfontconfig -lXinerama -ldl -lm -lX11
+ vbox-greeter_LDFLAGS += -s
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/Additions/linux/lightdm-greeter/banner-dummy.png b/src/VBox/Additions/linux/lightdm-greeter/banner-dummy.png
new file mode 100644
index 00000000..d74e9c1a
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/banner-dummy.png
Binary files differ
diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/Makefile.kmk b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/Makefile.kmk
new file mode 100644
index 00000000..e9950d3b
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/Makefile.kmk
@@ -0,0 +1,44 @@
+# $Id: Makefile.kmk $
+## @file
+# Makefile for liblighdm-gobject 1.5.0
+#
+
+#
+# Copyright (C) 2013-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# The greeter module.
+LIBRARIES += VBox-liblightdm-gobject
+
+VBox-liblightdm-gobject_TEMPLATE = VBOXGUESTR3NPLIB
+VBox-liblightdm-gobject_SDKS = VBoxGlib20WithIo
+VBox-liblightdm-gobject_INCS = \
+ /usr/include/glib-2.0 \
+ /usr/lib/i386-linux-gnu/glib-2.0/include \
+ /usr/lib/x86_64-linux-gnu/glib-2.0/include \
+ /usr/include/gio-unix-2.0
+VBox-liblightdm-gobject_DEFS = \
+ CONFIG_DIR=\"/etc/lightdm\" \
+ XSESSIONS_DIR=\"/usr/share/xsessions\" \
+ REMOTE_SESSIONS_DIR=\"/usr/share/lightdm/remote-sessions\"
+VBox-liblightdm-gobject_SOURCES = \
+ greeter.c \
+ language.c \
+ layout.c \
+ power.c \
+ session.c \
+ system.c \
+ user.c
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/config.h b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/config.h
new file mode 100644
index 00000000..c06fc08d
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/config.h
@@ -0,0 +1,90 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Gettext package */
+#define GETTEXT_PACKAGE "lightdm"
+
+/* Greeter session */
+#define GREETER_SESSION "default"
+
+/* User to run greeter as */
+#define GREETER_USER "lightdm"
+
+/* Define to 1 if you have the `clearenv' function. */
+#define HAVE_CLEARENV 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <security/pam_appl.h> header file. */
+#define HAVE_SECURITY_PAM_APPL_H 1
+
+/* Define to 1 if you have the `setresgid' function. */
+#define HAVE_SETRESGID 1
+
+/* Define to 1 if you have the `setresuid' function. */
+#define HAVE_SETRESUID 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Name of package */
+#define PACKAGE "lightdm"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "lightdm"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "lightdm 1.5.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "lightdm"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.5.0"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* User session */
+#define USER_SESSION "default"
+
+/* Version number of package */
+#define VERSION "1.5.0"
diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/greeter.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/greeter.c
new file mode 100644
index 00000000..3d44b562
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/greeter.c
@@ -0,0 +1,1442 @@
+/*
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+/*
+ * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
+ * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
+ * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
+ * a choice of LGPL license versions is made available with the language indicating
+ * that LGPLv2 or any later version may be used, or where a choice of which version
+ * of the LGPL is applied is otherwise unspecified.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <security/pam_appl.h>
+
+#include "lightdm/greeter.h"
+
+enum {
+ PROP_0,
+ PROP_DEFAULT_SESSION_HINT,
+ PROP_HIDE_USERS_HINT,
+ PROP_SHOW_MANUAL_LOGIN_HINT,
+ PROP_SHOW_REMOTE_LOGIN_HINT,
+ PROP_LOCK_HINT,
+ PROP_HAS_GUEST_ACCOUNT_HINT,
+ PROP_SELECT_USER_HINT,
+ PROP_SELECT_GUEST_HINT,
+ PROP_AUTOLOGIN_USER_HINT,
+ PROP_AUTOLOGIN_GUEST_HINT,
+ PROP_AUTOLOGIN_TIMEOUT_HINT,
+ PROP_AUTHENTICATION_USER,
+ PROP_IN_AUTHENTICATION,
+ PROP_IS_AUTHENTICATED,
+};
+
+enum {
+ SHOW_PROMPT,
+ SHOW_MESSAGE,
+ AUTHENTICATION_COMPLETE,
+ AUTOLOGIN_TIMER_EXPIRED,
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct
+{
+ gboolean connected;
+
+ GIOChannel *to_server_channel, *from_server_channel;
+ guint8 *read_buffer;
+ gsize n_read;
+
+ gsize n_responses_waiting;
+ GList *responses_received;
+
+ GHashTable *hints;
+ guint autologin_timeout;
+
+ gchar *authentication_user;
+ gboolean in_authentication;
+ gboolean is_authenticated;
+ guint32 authenticate_sequence_number;
+ gboolean cancelling_authentication;
+} LightDMGreeterPrivate;
+
+G_DEFINE_TYPE (LightDMGreeter, lightdm_greeter, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_GREETER, LightDMGreeterPrivate)
+
+#define HEADER_SIZE 8
+#define MAX_MESSAGE_LENGTH 1024
+
+/* Messages from the greeter to the server */
+typedef enum
+{
+ GREETER_MESSAGE_CONNECT = 0,
+ GREETER_MESSAGE_AUTHENTICATE,
+ GREETER_MESSAGE_AUTHENTICATE_AS_GUEST,
+ GREETER_MESSAGE_CONTINUE_AUTHENTICATION,
+ GREETER_MESSAGE_START_SESSION,
+ GREETER_MESSAGE_CANCEL_AUTHENTICATION,
+ GREETER_MESSAGE_SET_LANGUAGE,
+ GREETER_MESSAGE_AUTHENTICATE_REMOTE
+} GreeterMessage;
+
+/* Messages from the server to the greeter */
+typedef enum
+{
+ SERVER_MESSAGE_CONNECTED = 0,
+ SERVER_MESSAGE_PROMPT_AUTHENTICATION,
+ SERVER_MESSAGE_END_AUTHENTICATION,
+ SERVER_MESSAGE_SESSION_RESULT
+} ServerMessage;
+
+/**
+ * lightdm_greeter_new:
+ *
+ * Create a new greeter.
+ *
+ * Return value: the new #LightDMGreeter
+ **/
+LightDMGreeter *
+lightdm_greeter_new ()
+{
+ return g_object_new (LIGHTDM_TYPE_GREETER, NULL);
+}
+
+static gboolean
+timed_login_cb (gpointer data)
+{
+ LightDMGreeter *greeter = data;
+ LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+
+ priv->autologin_timeout = 0;
+ g_signal_emit (G_OBJECT (greeter), signals[AUTOLOGIN_TIMER_EXPIRED], 0);
+
+ return FALSE;
+}
+
+static guint32
+int_length ()
+{
+ return 4;
+}
+
+static void
+write_int (guint8 *buffer, gint buffer_length, guint32 value, gsize *offset)
+{
+ if (*offset + 4 >= buffer_length)
+ return;
+ buffer[*offset] = value >> 24;
+ buffer[*offset+1] = (value >> 16) & 0xFF;
+ buffer[*offset+2] = (value >> 8) & 0xFF;
+ buffer[*offset+3] = value & 0xFF;
+ *offset += 4;
+}
+
+static void
+write_string (guint8 *buffer, gint buffer_length, const gchar *value, gsize *offset)
+{
+ gint length = 0;
+
+ if (value)
+ length = strlen (value);
+ write_int (buffer, buffer_length, length, offset);
+ if (*offset + length >= buffer_length)
+ return;
+ memcpy (buffer + *offset, value, length);
+ *offset += length;
+}
+
+static guint32
+read_int (guint8 *message, gsize message_length, gsize *offset)
+{
+ guint32 value;
+ guint8 *buffer;
+
+ if (message_length - *offset < int_length ())
+ {
+ g_warning ("Not enough space for int, need %i, got %zi", int_length (), message_length - *offset);
+ return 0;
+ }
+
+ buffer = message + *offset;
+ value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
+ *offset += int_length ();
+
+ return value;
+}
+
+static gchar *
+read_string (guint8 *message, gsize message_length, gsize *offset)
+{
+ guint32 length;
+ gchar *value;
+
+ length = read_int (message, message_length, offset);
+ if (message_length - *offset < length)
+ {
+ g_warning ("Not enough space for string, need %u, got %zu", length, message_length - *offset);
+ return g_strdup ("");
+ }
+
+ value = g_malloc (sizeof (gchar) * (length + 1));
+ memcpy (value, message + *offset, length);
+ value[length] = '\0';
+ *offset += length;
+
+ return value;
+}
+
+static guint32
+string_length (const gchar *value)
+{
+ if (value)
+ return int_length () + strlen (value);
+ else
+ return int_length ();
+}
+
+static void
+write_header (guint8 *buffer, gint buffer_length, guint32 id, guint32 length, gsize *offset)
+{
+ write_int (buffer, buffer_length, id, offset);
+ write_int (buffer, buffer_length, length, offset);
+}
+
+static guint32
+get_message_length (guint8 *message, gsize message_length)
+{
+ gsize offset = 4;
+ return read_int (message, message_length, &offset);
+}
+
+static void
+write_message (LightDMGreeter *greeter, guint8 *message, gsize message_length)
+{
+ LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+ GIOStatus status;
+ GError *error = NULL;
+ guint32 stated_length;
+
+ /* Double check that we're sending well-formed messages. If we say we're
+ sending more than we do, we end up DOS'ing lightdm as it waits for the
+ rest. If we say we're sending less than we do, we confuse the heck out
+ of lightdm, as it starts reading headers from the middle of our
+ messages. */
+ stated_length = HEADER_SIZE + get_message_length (message, message_length);
+ if (stated_length != message_length)
+ {
+ g_warning ("Refusing to write malformed packet to daemon: declared size is %u, but actual size is %zu", stated_length, message_length);
+ return;
+ }
+
+ status = g_io_channel_write_chars (priv->to_server_channel, (gchar *) message, message_length, NULL, &error);
+ if (error)
+ g_warning ("Error writing to daemon: %s", error->message);
+ g_clear_error (&error);
+ if (status == G_IO_STATUS_NORMAL)
+ g_debug ("Wrote %zi bytes to daemon", message_length);
+ g_io_channel_flush (priv->to_server_channel, NULL);
+}
+
+static void
+handle_connected (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset)
+{
+ LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+ gchar *version;
+ GString *hint_string;
+ int timeout;
+
+ version = read_string (message, message_length, offset);
+ hint_string = g_string_new ("");
+ while (*offset < message_length)
+ {
+ gchar *name, *value;
+
+ name = read_string (message, message_length, offset);
+ value = read_string (message, message_length, offset);
+ g_hash_table_insert (priv->hints, name, value);
+ g_string_append_printf (hint_string, " %s=%s", name, value);
+ }
+
+ g_debug ("Connected version=%s%s", version, hint_string->str);
+ g_free (version);
+ g_string_free (hint_string, TRUE);
+
+ /* Set timeout for default login */
+ timeout = lightdm_greeter_get_autologin_timeout_hint (greeter);
+ if (timeout)
+ {
+ g_debug ("Setting autologin timer for %d seconds", timeout);
+ priv->autologin_timeout = g_timeout_add (timeout * 1000, timed_login_cb, greeter);
+ }
+}
+
+static void
+handle_prompt_authentication (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset)
+{
+ LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+ guint32 sequence_number, n_messages, i;
+ gchar *username;
+
+ sequence_number = read_int (message, message_length, offset);
+ if (sequence_number != priv->authenticate_sequence_number)
+ {
+ g_debug ("Ignoring prompt authentication with invalid sequence number %d", sequence_number);
+ return;
+ }
+
+ if (priv->cancelling_authentication)
+ {
+ g_debug ("Ignoring prompt authentication as waiting for it to cancel");
+ return;
+ }
+
+ /* Update username */
+ username = read_string (message, message_length, offset);
+ if (strcmp (username, "") == 0)
+ {
+ g_free (username);
+ username = NULL;
+ }
+ g_free (priv->authentication_user);
+ priv->authentication_user = username;
+
+ g_list_free_full (priv->responses_received, g_free);
+ priv->responses_received = NULL;
+ priv->n_responses_waiting = 0;
+
+ n_messages = read_int (message, message_length, offset);
+ g_debug ("Prompt user with %d message(s)", n_messages);
+
+ for (i = 0; i < n_messages; i++)
+ {
+ int style;
+ gchar *text;
+
+ style = read_int (message, message_length, offset);
+ text = read_string (message, message_length, offset);
+
+ // FIXME: Should stop on prompts?
+ switch (style)
+ {
+ case PAM_PROMPT_ECHO_OFF:
+ priv->n_responses_waiting++;
+ g_signal_emit (G_OBJECT (greeter), signals[SHOW_PROMPT], 0, text, LIGHTDM_PROMPT_TYPE_SECRET);
+ break;
+ case PAM_PROMPT_ECHO_ON:
+ priv->n_responses_waiting++;
+ g_signal_emit (G_OBJECT (greeter), signals[SHOW_PROMPT], 0, text, LIGHTDM_PROMPT_TYPE_QUESTION);
+ break;
+ case PAM_ERROR_MSG:
+ g_signal_emit (G_OBJECT (greeter), signals[SHOW_MESSAGE], 0, text, LIGHTDM_MESSAGE_TYPE_ERROR);
+ break;
+ case PAM_TEXT_INFO:
+ g_signal_emit (G_OBJECT (greeter), signals[SHOW_MESSAGE], 0, text, LIGHTDM_MESSAGE_TYPE_INFO);
+ break;
+ }
+
+ g_free (text);
+ }
+}
+
+static void
+handle_end_authentication (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset)
+{
+ LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+ guint32 sequence_number, return_code;
+ gchar *username;
+
+ sequence_number = read_int (message, message_length, offset);
+
+ if (sequence_number != priv->authenticate_sequence_number)
+ {
+ g_debug ("Ignoring end authentication with invalid sequence number %d", sequence_number);
+ return;
+ }
+
+ username = read_string (message, message_length, offset);
+ return_code = read_int (message, message_length, offset);
+
+ g_debug ("Authentication complete for user %s with return code %d", username, return_code);
+
+ /* Update username */
+ if (strcmp (username, "") == 0)
+ {
+ g_free (username);
+ username = NULL;
+ }
+ g_free (priv->authentication_user);
+ priv->authentication_user = username;
+
+ priv->cancelling_authentication = FALSE;
+ priv->is_authenticated = (return_code == 0);
+
+ priv->in_authentication = FALSE;
+ g_signal_emit (G_OBJECT (greeter), signals[AUTHENTICATION_COMPLETE], 0);
+}
+
+static guint8 *
+read_message (LightDMGreeter *greeter, gsize *length, gboolean block)
+{
+ LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+ gsize n_to_read, n_read;
+ guint8 *buffer;
+ GError *error = NULL;
+
+ /* Read the header, or the whole message if we already have that */
+ n_to_read = HEADER_SIZE;
+ if (priv->n_read >= HEADER_SIZE)
+ n_to_read += get_message_length (priv->read_buffer, priv->n_read);
+
+ do
+ {
+ GIOStatus status;
+ status = g_io_channel_read_chars (priv->from_server_channel,
+ (gchar *) priv->read_buffer + priv->n_read,
+ n_to_read - priv->n_read,
+ &n_read,
+ &error);
+ if (error)
+ g_warning ("Error reading from server: %s", error->message);
+ g_clear_error (&error);
+ if (status != G_IO_STATUS_NORMAL)
+ break;
+
+ g_debug ("Read %zi bytes from daemon", n_read);
+
+ priv->n_read += n_read;
+ } while (priv->n_read < n_to_read && block);
+
+ /* Stop if haven't got all the data we want */
+ if (priv->n_read != n_to_read)
+ return FALSE;
+
+ /* If have header, rerun for content */
+ if (priv->n_read == HEADER_SIZE)
+ {
+ n_to_read = get_message_length (priv->read_buffer, priv->n_read);
+ if (n_to_read > 0)
+ {
+ priv->read_buffer = g_realloc (priv->read_buffer, HEADER_SIZE + n_to_read);
+ return read_message (greeter, length, block);
+ }
+ }
+
+ buffer = priv->read_buffer;
+ *length = priv->n_read;
+
+ priv->read_buffer = g_malloc (priv->n_read);
+ priv->n_read = 0;
+
+ return buffer;
+}
+
+static gboolean
+from_server_cb (GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ LightDMGreeter *greeter = data;
+ guint8 *message;
+ gsize message_length, offset;
+ guint32 id;
+
+ message = read_message (greeter, &message_length, FALSE);
+ if (!message)
+ return TRUE;
+
+ offset = 0;
+ id = read_int (message, message_length, &offset);
+ read_int (message, message_length, &offset);
+ switch (id)
+ {
+ case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
+ handle_prompt_authentication (greeter, message, message_length, &offset);
+ break;
+ case SERVER_MESSAGE_END_AUTHENTICATION:
+ handle_end_authentication (greeter, message, message_length, &offset);
+ break;
+ default:
+ g_warning ("Unknown message from server: %d", id);
+ break;
+ }
+ g_free (message);
+
+ return TRUE;
+}
+
+/**
+ * lightdm_greeter_connect_sync:
+ * @greeter: The greeter to connect
+ * @error: return location for a #GError, or %NULL
+ *
+ * Connects the greeter to the display manager. Will block until connected.
+ *
+ * Return value: #TRUE if successfully connected
+ **/
+gboolean
+lightdm_greeter_connect_sync (LightDMGreeter *greeter, GError **error)
+{
+ LightDMGreeterPrivate *priv;
+ const gchar *fd;
+ guint8 message[MAX_MESSAGE_LENGTH];
+ guint8 *response;
+ gsize response_length, offset = 0;
+ guint32 id;
+
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+
+ priv = GET_PRIVATE (greeter);
+
+ fd = g_getenv ("LIGHTDM_TO_SERVER_FD");
+ if (!fd)
+ {
+ g_warning ("No LIGHTDM_TO_SERVER_FD environment variable");
+ return FALSE;
+ }
+ priv->to_server_channel = g_io_channel_unix_new (atoi (fd));
+ g_io_channel_set_encoding (priv->to_server_channel, NULL, NULL);
+
+ fd = g_getenv ("LIGHTDM_FROM_SERVER_FD");
+ if (!fd)
+ {
+ g_warning ("No LIGHTDM_FROM_SERVER_FD environment variable");
+ return FALSE;
+ }
+ priv->from_server_channel = g_io_channel_unix_new (atoi (fd));
+ g_io_channel_set_encoding (priv->from_server_channel, NULL, NULL);
+ g_io_add_watch (priv->from_server_channel, G_IO_IN, from_server_cb, greeter);
+
+ g_debug ("Connecting to display manager...");
+ write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONNECT, string_length (VERSION), &offset);
+ write_string (message, MAX_MESSAGE_LENGTH, VERSION, &offset);
+ write_message (greeter, message, offset);
+
+ response = read_message (greeter, &response_length, TRUE);
+ if (!response)
+ return FALSE;
+
+ offset = 0;
+ id = read_int (response, response_length, &offset);
+ read_int (response, response_length, &offset);
+ if (id == SERVER_MESSAGE_CONNECTED)
+ handle_connected (greeter, response, response_length, &offset);
+ g_free (response);
+ if (id != SERVER_MESSAGE_CONNECTED)
+ {
+ g_warning ("Expected CONNECTED message, got %d", id);
+ return FALSE;
+ }
+
+ priv->connected = TRUE;
+
+ return TRUE;
+}
+
+/**
+ * lightdm_greeter_get_hint:
+ * @greeter: A #LightDMGreeter
+ * @name: The hint name to query.
+ *
+ * Get a hint.
+ *
+ * Return value: The value for this hint or #NULL if not set.
+ **/
+const gchar *
+lightdm_greeter_get_hint (LightDMGreeter *greeter, const gchar *name)
+{
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
+ return g_hash_table_lookup (GET_PRIVATE (greeter)->hints, name);
+}
+
+/**
+ * lightdm_greeter_get_default_session_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Get the default session to use.
+ *
+ * Return value: The session name
+ **/
+const gchar *
+lightdm_greeter_get_default_session_hint (LightDMGreeter *greeter)
+{
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
+ return lightdm_greeter_get_hint (greeter, "default-session");
+}
+
+/**
+ * lightdm_greeter_get_hide_users_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if user accounts should be shown. If this is TRUE then the list of
+ * accounts should be taken from #LightDMUserList and displayed in the greeter
+ * for the user to choose from. Note that this list can be empty and it is
+ * recommended you show a method for the user to enter a username manually.
+ *
+ * If this option is shown the greeter should only allow these users to be
+ * chosen for login unless the manual login hint is set.
+ *
+ * Return value: #TRUE if the available users should not be shown.
+ */
+gboolean
+lightdm_greeter_get_hide_users_hint (LightDMGreeter *greeter)
+{
+ const gchar *value;
+
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+ value = lightdm_greeter_get_hint (greeter, "hide-users");
+
+ return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_show_manual_login_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if a manual login option should be shown. If set the GUI
+ * should provide a way for a username to be entered manually.
+ * Without this hint a greeter which is showing a user list can
+ * limit logins to only those users.
+ *
+ * Return value: #TRUE if a manual login option should be shown.
+ */
+gboolean
+lightdm_greeter_get_show_manual_login_hint (LightDMGreeter *greeter)
+{
+ const gchar *value;
+
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+ value = lightdm_greeter_get_hint (greeter, "show-manual-login");
+
+ return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_show_remote_login_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if a remote login option should be shown. If set the GUI
+ * should provide a way for a user to log into a remote desktop server.
+ *
+ * Return value: #TRUE if a remote login option should be shown.
+ */
+gboolean
+lightdm_greeter_get_show_remote_login_hint (LightDMGreeter *greeter)
+{
+ const gchar *value;
+
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+ value = lightdm_greeter_get_hint (greeter, "show-remote-login");
+
+ return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_lock_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if the greeter is acting as a lock screen.
+ *
+ * Return value: #TRUE if the greeter was triggered by locking the seat.
+ */
+gboolean
+lightdm_greeter_get_lock_hint (LightDMGreeter *greeter)
+{
+ const gchar *value;
+
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+ value = lightdm_greeter_get_hint (greeter, "lock-screen");
+
+ return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_has_guest_account_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if guest sessions are supported.
+ *
+ * Return value: #TRUE if guest sessions are supported.
+ */
+gboolean
+lightdm_greeter_get_has_guest_account_hint (LightDMGreeter *greeter)
+{
+ const gchar *value;
+
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+ value = lightdm_greeter_get_hint (greeter, "has-guest-account");
+
+ return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_select_user_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Get the user to select by default.
+ *
+ * Return value: A username
+ */
+const gchar *
+lightdm_greeter_get_select_user_hint (LightDMGreeter *greeter)
+{
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
+ return lightdm_greeter_get_hint (greeter, "select-user");
+}
+
+/**
+ * lightdm_greeter_get_select_guest_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if the guest account should be selected by default.
+ *
+ * Return value: #TRUE if the guest account should be selected by default.
+ */
+gboolean
+lightdm_greeter_get_select_guest_hint (LightDMGreeter *greeter)
+{
+ const gchar *value;
+
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+ value = lightdm_greeter_get_hint (greeter, "select-guest");
+
+ return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_autologin_user_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Get the user account to automatically logg into when the timer expires.
+ *
+ * Return value: The user account to automatically log into.
+ */
+const gchar *
+lightdm_greeter_get_autologin_user_hint (LightDMGreeter *greeter)
+{
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
+ return lightdm_greeter_get_hint (greeter, "autologin-user");
+}
+
+/**
+ * lightdm_greeter_get_autologin_guest_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if the guest account should be automatically logged into when the timer expires.
+ *
+ * Return value: #TRUE if the guest account should be automatically logged into.
+ */
+gboolean
+lightdm_greeter_get_autologin_guest_hint (LightDMGreeter *greeter)
+{
+ const gchar *value;
+
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+ value = lightdm_greeter_get_hint (greeter, "autologin-guest");
+
+ return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_autologin_timeout_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Get the number of seconds to wait before automaitcally logging in.
+ *
+ * Return value: The number of seconds to wait before automatically logging in or 0 for no timeout.
+ */
+gint
+lightdm_greeter_get_autologin_timeout_hint (LightDMGreeter *greeter)
+{
+ const gchar *value;
+ gint timeout = 0;
+
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+ value = lightdm_greeter_get_hint (greeter, "autologin-timeout");
+ if (value)
+ timeout = atoi (value);
+ if (timeout < 0)
+ timeout = 0;
+
+ return timeout;
+}
+
+/**
+ * lightdm_greeter_cancel_autologin:
+ * @greeter: A #LightDMGreeter
+ *
+ * Cancel the automatic login.
+ */
+void
+lightdm_greeter_cancel_autologin (LightDMGreeter *greeter)
+{
+ LightDMGreeterPrivate *priv;
+
+ g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+ priv = GET_PRIVATE (greeter);
+
+ if (priv->autologin_timeout)
+ g_source_remove (priv->autologin_timeout);
+ priv->autologin_timeout = 0;
+}
+
+/**
+ * lightdm_greeter_authenticate:
+ * @greeter: A #LightDMGreeter
+ * @username: (allow-none): A username or #NULL to prompt for a username.
+ *
+ * Starts the authentication procedure for a user.
+ **/
+void
+lightdm_greeter_authenticate (LightDMGreeter *greeter, const gchar *username)
+{
+ LightDMGreeterPrivate *priv;
+ guint8 message[MAX_MESSAGE_LENGTH];
+ gsize offset = 0;
+
+ g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+ priv = GET_PRIVATE (greeter);
+
+ g_return_if_fail (priv->connected);
+
+ priv->cancelling_authentication = FALSE;
+ priv->authenticate_sequence_number++;
+ priv->in_authentication = TRUE;
+ priv->is_authenticated = FALSE;
+ if (username != priv->authentication_user)
+ {
+ g_free (priv->authentication_user);
+ priv->authentication_user = g_strdup (username);
+ }
+
+ g_debug ("Starting authentication for user %s...", username);
+ write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE, int_length () + string_length (username), &offset);
+ write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset);
+ write_string (message, MAX_MESSAGE_LENGTH, username, &offset);
+ write_message (greeter, message, offset);
+}
+
+/**
+ * lightdm_greeter_authenticate_as_guest:
+ * @greeter: A #LightDMGreeter
+ *
+ * Starts the authentication procedure for the guest user.
+ **/
+void
+lightdm_greeter_authenticate_as_guest (LightDMGreeter *greeter)
+{
+ LightDMGreeterPrivate *priv;
+ guint8 message[MAX_MESSAGE_LENGTH];
+ gsize offset = 0;
+
+ g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+ priv = GET_PRIVATE (greeter);
+
+ g_return_if_fail (priv->connected);
+
+ priv->cancelling_authentication = FALSE;
+ priv->authenticate_sequence_number++;
+ priv->in_authentication = TRUE;
+ priv->is_authenticated = FALSE;
+ g_free (priv->authentication_user);
+ priv->authentication_user = NULL;
+
+ g_debug ("Starting authentication for guest account...");
+ write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE_AS_GUEST, int_length (), &offset);
+ write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset);
+ write_message (greeter, message, offset);
+}
+
+/**
+ * lightdm_greeter_authenticate_autologin:
+ * @greeter: A #LightDMGreeter
+ *
+ * Starts the authentication procedure for the automatic login user.
+ **/
+void
+lightdm_greeter_authenticate_autologin (LightDMGreeter *greeter)
+{
+ const gchar *user;
+
+ user = lightdm_greeter_get_autologin_user_hint (greeter);
+ if (lightdm_greeter_get_autologin_guest_hint (greeter))
+ lightdm_greeter_authenticate_as_guest (greeter);
+ else if (user)
+ lightdm_greeter_authenticate (greeter, user);
+}
+
+/**
+ * lightdm_greeter_authenticate_remote:
+ * @greeter: A #LightDMGreeter
+ * @session: The name of a remote session
+ * @username: (allow-none): A username of #NULL to prompt for a username.
+ *
+ * Start authentication for a remote session type.
+ **/
+void
+lightdm_greeter_authenticate_remote (LightDMGreeter *greeter, const gchar *session, const gchar *username)
+{
+ LightDMGreeterPrivate *priv;
+ guint8 message[MAX_MESSAGE_LENGTH];
+ gsize offset = 0;
+
+ g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+ priv = GET_PRIVATE (greeter);
+
+ g_return_if_fail (priv->connected);
+
+ priv->cancelling_authentication = FALSE;
+ priv->authenticate_sequence_number++;
+ priv->in_authentication = TRUE;
+ priv->is_authenticated = FALSE;
+ g_free (priv->authentication_user);
+ priv->authentication_user = NULL;
+
+ if (username)
+ g_debug ("Starting authentication for remote session %s as user %s...", session, username);
+ else
+ g_debug ("Starting authentication for remote session %s...", session);
+ write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE_REMOTE, int_length () + string_length (session) + string_length (username), &offset);
+ write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset);
+ write_string (message, MAX_MESSAGE_LENGTH, session, &offset);
+ write_string (message, MAX_MESSAGE_LENGTH, username, &offset);
+ write_message (greeter, message, offset);
+}
+
+/**
+ * lightdm_greeter_respond:
+ * @greeter: A #LightDMGreeter
+ * @response: Response to a prompt
+ *
+ * Provide response to a prompt. May be one in a series.
+ **/
+void
+lightdm_greeter_respond (LightDMGreeter *greeter, const gchar *response)
+{
+ LightDMGreeterPrivate *priv;
+ guint8 message[MAX_MESSAGE_LENGTH];
+ gsize offset = 0;
+
+ g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+ g_return_if_fail (response != NULL);
+
+ priv = GET_PRIVATE (greeter);
+
+ g_return_if_fail (priv->connected);
+ g_return_if_fail (priv->n_responses_waiting > 0);
+
+ priv->n_responses_waiting--;
+ priv->responses_received = g_list_append (priv->responses_received, g_strdup (response));
+
+ if (priv->n_responses_waiting == 0)
+ {
+ guint32 msg_length;
+ GList *iter;
+
+ g_debug ("Providing response to display manager");
+
+ msg_length = int_length ();
+ for (iter = priv->responses_received; iter; iter = iter->next)
+ {
+ msg_length += string_length ((gchar *)iter->data);
+ }
+
+ write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONTINUE_AUTHENTICATION, msg_length, &offset);
+ write_int (message, MAX_MESSAGE_LENGTH, g_list_length (priv->responses_received), &offset);
+ for (iter = priv->responses_received; iter; iter = iter->next)
+ {
+ write_string (message, MAX_MESSAGE_LENGTH, (gchar *)iter->data, &offset);
+ }
+ write_message (greeter, message, offset);
+
+ g_list_free_full (priv->responses_received, g_free);
+ priv->responses_received = NULL;
+ }
+}
+
+/**
+ * lightdm_greeter_cancel_authentication:
+ * @greeter: A #LightDMGreeter
+ *
+ * Cancel the current user authentication.
+ **/
+void
+lightdm_greeter_cancel_authentication (LightDMGreeter *greeter)
+{
+ LightDMGreeterPrivate *priv;
+ guint8 message[MAX_MESSAGE_LENGTH];
+ gsize offset = 0;
+
+ g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+ priv = GET_PRIVATE (greeter);
+
+ g_return_if_fail (priv->connected);
+
+ priv->cancelling_authentication = TRUE;
+ write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0, &offset);
+ write_message (greeter, message, offset);
+}
+
+/**
+ * lightdm_greeter_get_in_authentication:
+ * @greeter: A #LightDMGreeter
+ *
+ * Checks if the greeter is in the process of authenticating.
+ *
+ * Return value: #TRUE if the greeter is authenticating a user.
+ **/
+gboolean
+lightdm_greeter_get_in_authentication (LightDMGreeter *greeter)
+{
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+ return GET_PRIVATE (greeter)->in_authentication;
+}
+
+/**
+ * lightdm_greeter_get_is_authenticated:
+ * @greeter: A #LightDMGreeter
+ *
+ * Checks if the greeter has successfully authenticated.
+ *
+ * Return value: #TRUE if the greeter is authenticated for login.
+ **/
+gboolean
+lightdm_greeter_get_is_authenticated (LightDMGreeter *greeter)
+{
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+ return GET_PRIVATE (greeter)->is_authenticated;
+}
+
+/**
+ * lightdm_greeter_get_authentication_user:
+ * @greeter: A #LightDMGreeter
+ *
+ * Get the user that is being authenticated.
+ *
+ * Return value: The username of the authentication user being authenticated or #NULL if no authentication in progress.
+ */
+const gchar *
+lightdm_greeter_get_authentication_user (LightDMGreeter *greeter)
+{
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
+ return GET_PRIVATE (greeter)->authentication_user;
+}
+
+/**
+ * lightdm_greeter_set_language:
+ * @greeter: A #LightDMGreeter
+ * @language: The language to use for this user.
+ *
+ * Set the language for the currently authenticated user.
+ **/
+void
+lightdm_greeter_set_language (LightDMGreeter *greeter, const gchar *language)
+{
+ LightDMGreeterPrivate *priv;
+ guint8 message[MAX_MESSAGE_LENGTH];
+ gsize offset = 0;
+
+ g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+ priv = GET_PRIVATE (greeter);
+
+ g_return_if_fail (priv->connected);
+
+ write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_SET_LANGUAGE, string_length (language), &offset);
+ write_string (message, MAX_MESSAGE_LENGTH, language, &offset);
+ write_message (greeter, message, offset);
+}
+
+/**
+ * lightdm_greeter_start_session_sync:
+ * @greeter: A #LightDMGreeter
+ * @session: (allow-none): The session to log into or #NULL to use the default.
+ * @error: return location for a #GError, or %NULL
+ *
+ * Start a session for the authenticated user.
+ *
+ * Return value: TRUE if the session was started.
+ **/
+gboolean
+lightdm_greeter_start_session_sync (LightDMGreeter *greeter, const gchar *session, GError **error)
+{
+ LightDMGreeterPrivate *priv;
+ guint8 message[MAX_MESSAGE_LENGTH];
+ guint8 *response;
+ gsize response_length, offset = 0;
+ guint32 id, return_code = 1;
+
+ g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+
+ priv = GET_PRIVATE (greeter);
+
+ g_return_val_if_fail (priv->connected, FALSE);
+ g_return_val_if_fail (priv->is_authenticated, FALSE);
+
+ if (session)
+ g_debug ("Starting session %s", session);
+ else
+ g_debug ("Starting default session");
+
+ write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_START_SESSION, string_length (session), &offset);
+ write_string (message, MAX_MESSAGE_LENGTH, session, &offset);
+ write_message (greeter, message, offset);
+
+ response = read_message (greeter, &response_length, TRUE);
+ if (!response)
+ return FALSE;
+
+ offset = 0;
+ id = read_int (response, response_length, &offset);
+ read_int (response, response_length, &offset);
+ if (id == SERVER_MESSAGE_SESSION_RESULT)
+ return_code = read_int (response, response_length, &offset);
+ else
+ g_warning ("Expected SESSION_RESULT message, got %d", id);
+
+ g_free (response);
+
+ return return_code == 0;
+}
+
+static void
+lightdm_greeter_init (LightDMGreeter *greeter)
+{
+ LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+
+ priv->read_buffer = g_malloc (HEADER_SIZE);
+ priv->hints = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+}
+
+static void
+lightdm_greeter_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+lightdm_greeter_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ LightDMGreeter *self;
+
+ self = LIGHTDM_GREETER (object);
+
+ switch (prop_id) {
+ case PROP_DEFAULT_SESSION_HINT:
+ g_value_set_string (value, lightdm_greeter_get_default_session_hint (self));
+ break;
+ case PROP_HIDE_USERS_HINT:
+ g_value_set_boolean (value, lightdm_greeter_get_hide_users_hint (self));
+ break;
+ case PROP_SHOW_MANUAL_LOGIN_HINT:
+ g_value_set_boolean (value, lightdm_greeter_get_show_manual_login_hint (self));
+ break;
+ case PROP_SHOW_REMOTE_LOGIN_HINT:
+ g_value_set_boolean (value, lightdm_greeter_get_show_remote_login_hint (self));
+ break;
+ case PROP_LOCK_HINT:
+ g_value_set_boolean (value, lightdm_greeter_get_lock_hint (self));
+ break;
+ case PROP_HAS_GUEST_ACCOUNT_HINT:
+ g_value_set_boolean (value, lightdm_greeter_get_has_guest_account_hint (self));
+ break;
+ case PROP_SELECT_USER_HINT:
+ g_value_set_string (value, lightdm_greeter_get_select_user_hint (self));
+ break;
+ case PROP_SELECT_GUEST_HINT:
+ g_value_set_boolean (value, lightdm_greeter_get_select_guest_hint (self));
+ break;
+ case PROP_AUTOLOGIN_USER_HINT:
+ g_value_set_string (value, lightdm_greeter_get_autologin_user_hint (self));
+ break;
+ case PROP_AUTOLOGIN_GUEST_HINT:
+ g_value_set_boolean (value, lightdm_greeter_get_autologin_guest_hint (self));
+ break;
+ case PROP_AUTOLOGIN_TIMEOUT_HINT:
+ g_value_set_int (value, lightdm_greeter_get_autologin_timeout_hint (self));
+ break;
+ case PROP_AUTHENTICATION_USER:
+ g_value_set_string (value, lightdm_greeter_get_authentication_user (self));
+ break;
+ case PROP_IN_AUTHENTICATION:
+ g_value_set_boolean (value, lightdm_greeter_get_in_authentication (self));
+ break;
+ case PROP_IS_AUTHENTICATED:
+ g_value_set_boolean (value, lightdm_greeter_get_is_authenticated (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+marshal_VOID__STRING_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_INT) (gpointer data1,
+ gpointer arg_1,
+ gint arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__STRING_INT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_INT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ (param_values + 1)->data[0].v_pointer,
+ (param_values + 2)->data[0].v_int,
+ data2);
+}
+
+static void
+lightdm_greeter_finalize (GObject *object)
+{
+ LightDMGreeter *self = LIGHTDM_GREETER (object);
+ LightDMGreeterPrivate *priv = GET_PRIVATE (self);
+
+ if (priv->to_server_channel)
+ g_io_channel_unref (priv->to_server_channel);
+ if (priv->from_server_channel)
+ g_io_channel_unref (priv->from_server_channel);
+ g_free (priv->authentication_user);
+ g_hash_table_unref (priv->hints);
+
+ G_OBJECT_CLASS (lightdm_greeter_parent_class)->finalize (object);
+}
+
+static void
+lightdm_greeter_class_init (LightDMGreeterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (LightDMGreeterPrivate));
+
+ object_class->set_property = lightdm_greeter_set_property;
+ object_class->get_property = lightdm_greeter_get_property;
+ object_class->finalize = lightdm_greeter_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_DEFAULT_SESSION_HINT,
+ g_param_spec_string ("default-session-hint",
+ "default-session-hint",
+ "Default session hint",
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_HIDE_USERS_HINT,
+ g_param_spec_boolean ("hide-users-hint",
+ "hide-users-hint",
+ "Hide users hint",
+ FALSE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_SHOW_MANUAL_LOGIN_HINT,
+ g_param_spec_boolean ("show-manual-login-hint",
+ "show-manual-login-hint",
+ "Show manual login hint",
+ FALSE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_SHOW_REMOTE_LOGIN_HINT,
+ g_param_spec_boolean ("show-remote-login-hint",
+ "show-remote-login-hint",
+ "Show remote login hint",
+ FALSE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_LOCK_HINT,
+ g_param_spec_boolean ("lock-hint",
+ "lock-hint",
+ "Lock hint",
+ FALSE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_HAS_GUEST_ACCOUNT_HINT,
+ g_param_spec_boolean ("has-guest-account-hint",
+ "has-guest-account-hint",
+ "Has guest account hint",
+ FALSE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_SELECT_USER_HINT,
+ g_param_spec_string ("select-user-hint",
+ "select-user-hint",
+ "Select user hint",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_SELECT_GUEST_HINT,
+ g_param_spec_boolean ("select-guest-hint",
+ "select-guest-hint",
+ "Select guest account hint",
+ FALSE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_AUTOLOGIN_USER_HINT,
+ g_param_spec_string ("autologin-user-hint",
+ "autologin-user-hint",
+ "Autologin user hint",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_AUTOLOGIN_GUEST_HINT,
+ g_param_spec_boolean ("autologin-guest-hint",
+ "autologin-guest-hint",
+ "Autologin guest account hint",
+ FALSE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_AUTOLOGIN_TIMEOUT_HINT,
+ g_param_spec_int ("autologin-timeout-hint",
+ "autologin-timeout-hint",
+ "Autologin timeout hint",
+ 0, G_MAXINT, 0,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_AUTHENTICATION_USER,
+ g_param_spec_string ("authentication-user",
+ "authentication-user",
+ "The user being authenticated",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_IN_AUTHENTICATION,
+ g_param_spec_boolean ("in-authentication",
+ "in-authentication",
+ "TRUE if a user is being authenticated",
+ FALSE,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_IS_AUTHENTICATED,
+ g_param_spec_boolean ("is-authenticated",
+ "is-authenticated",
+ "TRUE if the selected user is authenticated",
+ FALSE,
+ G_PARAM_READABLE));
+
+ /**
+ * LightDMGreeter::show-prompt:
+ * @greeter: A #LightDMGreeter
+ * @text: Prompt text
+ * @type: Prompt type
+ *
+ * The ::show-prompt signal gets emitted when the greeter should show a
+ * prompt to the user. The given text should be displayed and an input
+ * field for the user to provide a response.
+ *
+ * Call lightdm_greeter_respond() with the resultant input or
+ * lightdm_greeter_cancel_authentication() to abort the authentication.
+ **/
+ signals[SHOW_PROMPT] =
+ g_signal_new ("show-prompt",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (LightDMGreeterClass, show_prompt),
+ NULL, NULL,
+ marshal_VOID__STRING_INT,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
+
+ /**
+ * LightDMGreeter::show-message:
+ * @greeter: A #LightDMGreeter
+ * @text: Message text
+ * @type: Message type
+ *
+ * The ::show-message signal gets emitted when the greeter
+ * should show a message to the user.
+ **/
+ signals[SHOW_MESSAGE] =
+ g_signal_new ("show-message",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (LightDMGreeterClass, show_message),
+ NULL, NULL,
+ marshal_VOID__STRING_INT,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
+
+ /**
+ * LightDMGreeter::authentication-complete:
+ * @greeter: A #LightDMGreeter
+ *
+ * The ::authentication-complete signal gets emitted when the greeter
+ * has completed authentication.
+ *
+ * Call lightdm_greeter_get_is_authenticated() to check if the authentication
+ * was successful.
+ **/
+ signals[AUTHENTICATION_COMPLETE] =
+ g_signal_new ("authentication-complete",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (LightDMGreeterClass, authentication_complete),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * LightDMGreeter::autologin-timer-expired:
+ * @greeter: A #LightDMGreeter
+ *
+ * The ::timed-login signal gets emitted when the automatic login timer has expired.
+ * The application should then call lightdm_greeter_login().
+ **/
+ signals[AUTOLOGIN_TIMER_EXPIRED] =
+ g_signal_new ("autologin-timer-expired",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (LightDMGreeterClass, autologin_timer_expired),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/language.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/language.c
new file mode 100644
index 00000000..9709619e
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/language.c
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+/*
+ * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
+ * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
+ * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
+ * a choice of LGPL license versions is made available with the language indicating
+ * that LGPLv2 or any later version may be used, or where a choice of which version
+ * of the LGPL is applied is otherwise unspecified.
+ */
+
+#include <string.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <stdio.h>
+#include <glib/gi18n.h>
+
+#include "lightdm/language.h"
+
+enum {
+ PROP_0,
+ PROP_CODE,
+ PROP_NAME,
+ PROP_TERRITORY
+};
+
+typedef struct
+{
+ gchar *code;
+ gchar *name;
+ gchar *territory;
+} LightDMLanguagePrivate;
+
+G_DEFINE_TYPE (LightDMLanguage, lightdm_language, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_LANGUAGE, LightDMLanguagePrivate)
+
+static gboolean have_languages = FALSE;
+static GList *languages = NULL;
+
+static void
+update_languages (void)
+{
+ gchar *command = "locale -a";
+ gchar *stdout_text = NULL, *stderr_text = NULL;
+ gint exit_status;
+ gboolean result;
+ GError *error = NULL;
+
+ if (have_languages)
+ return;
+
+ result = g_spawn_command_line_sync (command, &stdout_text, &stderr_text, &exit_status, &error);
+ if (error)
+ {
+ g_warning ("Failed to run '%s': %s", command, error->message);
+ g_clear_error (&error);
+ }
+ else if (exit_status != 0)
+ g_warning ("Failed to get languages, '%s' returned %d", command, exit_status);
+ else if (result)
+ {
+ gchar **tokens;
+ int i;
+
+ tokens = g_strsplit_set (stdout_text, "\n\r", -1);
+ for (i = 0; tokens[i]; i++)
+ {
+ LightDMLanguage *language;
+ gchar *code;
+
+ code = g_strchug (tokens[i]);
+ if (code[0] == '\0')
+ continue;
+
+ /* Ignore the non-interesting languages */
+ if (strcmp (command, "locale -a") == 0 && !g_strrstr (code, ".utf8"))
+ continue;
+
+ language = g_object_new (LIGHTDM_TYPE_LANGUAGE, "code", code, NULL);
+ languages = g_list_append (languages, language);
+ }
+
+ g_strfreev (tokens);
+ }
+
+ g_free (stdout_text);
+ g_free (stderr_text);
+
+ have_languages = TRUE;
+}
+
+static gboolean
+is_utf8 (const gchar *code)
+{
+ return g_strrstr (code, ".utf8") || g_strrstr (code, ".UTF-8");
+}
+
+/* Get a valid locale name that can be passed to setlocale(), so we always can use nl_langinfo() to get language and country names. */
+static gchar *
+get_locale_name (const gchar *code)
+{
+ gchar *locale = NULL, *language;
+ char *at;
+ static gchar **avail_locales;
+ gint i;
+
+ if (is_utf8 (code))
+ return (gchar *) code;
+
+ if ((at = strchr (code, '@')))
+ language = g_strndup (code, at - code);
+ else
+ language = g_strdup (code);
+
+ if (!avail_locales)
+ {
+ gchar *locales;
+ GError *error = NULL;
+
+ if (g_spawn_command_line_sync ("locale -a", &locales, NULL, NULL, &error))
+ {
+ avail_locales = g_strsplit (g_strchomp (locales), "\n", -1);
+ g_free (locales);
+ }
+ else
+ {
+ g_warning ("Failed to run 'locale -a': %s", error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ if (avail_locales)
+ {
+ for (i = 0; avail_locales[i]; i++)
+ {
+ gchar *loc = avail_locales[i];
+ if (!g_strrstr (loc, ".utf8"))
+ continue;
+ if (g_str_has_prefix (loc, language))
+ {
+ locale = g_strdup (loc);
+ break;
+ }
+ }
+ }
+
+ g_free (language);
+
+ return locale;
+}
+
+/**
+ * lightdm_get_language:
+ *
+ * Get the current language.
+ *
+ * Return value: (transfer none): The current language or #NULL if no language.
+ **/
+LightDMLanguage *
+lightdm_get_language (void)
+{
+ const gchar *lang;
+ GList *link;
+
+ lang = g_getenv ("LANG");
+ if (!lang)
+ return NULL;
+
+ for (link = lightdm_get_languages (); link; link = link->next)
+ {
+ LightDMLanguage *language = link->data;
+ if (lightdm_language_matches (language, lang))
+ return language;
+ }
+
+ return NULL;
+}
+
+/**
+ * lightdm_get_languages:
+ *
+ * Get a list of languages to present to the user.
+ *
+ * Return value: (element-type LightDMLanguage) (transfer none): A list of #LightDMLanguage that should be presented to the user.
+ **/
+GList *
+lightdm_get_languages (void)
+{
+ update_languages ();
+ return languages;
+}
+
+/**
+ * lightdm_language_get_code:
+ * @language: A #LightDMLanguage
+ *
+ * Get the code of a language.
+ *
+ * Return value: The code of the language
+ **/
+const gchar *
+lightdm_language_get_code (LightDMLanguage *language)
+{
+ g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
+ return GET_PRIVATE (language)->code;
+}
+
+/**
+ * lightdm_language_get_name:
+ * @language: A #LightDMLanguage
+ *
+ * Get the name of a language.
+ *
+ * Return value: The name of the language
+ **/
+const gchar *
+lightdm_language_get_name (LightDMLanguage *language)
+{
+ LightDMLanguagePrivate *priv;
+
+ g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
+
+ priv = GET_PRIVATE (language);
+
+ if (!priv->name)
+ {
+ gchar *locale = get_locale_name (priv->code);
+ if (locale)
+ {
+ gchar *current = setlocale (LC_ALL, NULL);
+ setlocale (LC_IDENTIFICATION, locale);
+ setlocale (LC_MESSAGES, "");
+
+ gchar *language_en = nl_langinfo (_NL_IDENTIFICATION_LANGUAGE);
+ if (language_en && strlen (language_en) > 0)
+ priv->name = g_strdup (dgettext ("iso_639_3", language_en));
+
+ setlocale (LC_ALL, current);
+ }
+ if (!priv->name)
+ {
+ gchar **tokens = g_strsplit_set (priv->code, "_.@", 2);
+ priv->name = g_strdup (tokens[0]);
+ g_strfreev (tokens);
+ }
+ }
+
+ return priv->name;
+}
+
+/**
+ * lightdm_language_get_territory:
+ * @language: A #LightDMLanguage
+ *
+ * Get the territory the language is used in.
+ *
+ * Return value: The territory the language is used in.
+ **/
+const gchar *
+lightdm_language_get_territory (LightDMLanguage *language)
+{
+ LightDMLanguagePrivate *priv;
+
+ g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
+
+ priv = GET_PRIVATE (language);
+
+ if (!priv->territory && strchr (priv->code, '_'))
+ {
+ gchar *locale = get_locale_name (priv->code);
+ if (locale)
+ {
+ gchar *current = setlocale (LC_ALL, NULL);
+ setlocale (LC_IDENTIFICATION, locale);
+ setlocale (LC_MESSAGES, "");
+
+ gchar *country_en = nl_langinfo (_NL_IDENTIFICATION_TERRITORY);
+ if (country_en && strlen (country_en) > 0 && g_strcmp0 (country_en, "ISO") != 0)
+ priv->territory = g_strdup (dgettext ("iso_3166", country_en));
+
+ setlocale (LC_ALL, current);
+ }
+ if (!priv->territory)
+ {
+ gchar **tokens = g_strsplit_set (priv->code, "_.@", 3);
+ priv->territory = g_strdup (tokens[1]);
+ g_strfreev (tokens);
+ }
+ }
+
+ return priv->territory;
+}
+
+/**
+ * lightdm_language_matches:
+ * @language: A #LightDMLanguage
+ * @code: A language code
+ *
+ * Check if a language code matches this language.
+ *
+ * Return value: #TRUE if the code matches this language.
+ **/
+gboolean
+lightdm_language_matches (LightDMLanguage *language, const gchar *code)
+{
+ LightDMLanguagePrivate *priv;
+
+ g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), FALSE);
+ g_return_val_if_fail (code != NULL, FALSE);
+
+ priv = GET_PRIVATE (language);
+
+ /* Handle the fact the UTF-8 is specified both as '.utf8' and '.UTF-8' */
+ if (is_utf8 (priv->code) && is_utf8 (code))
+ {
+ /* Match the characters before the '.' */
+ int i;
+ for (i = 0; priv->code[i] && code[i] && priv->code[i] == code[i] && code[i] != '.' ; i++);
+ return priv->code[i] == '.' && code[i] == '.';
+ }
+
+ return g_str_equal (priv->code, code);
+}
+
+static void
+lightdm_language_init (LightDMLanguage *language)
+{
+}
+
+static void
+lightdm_language_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ LightDMLanguage *self = LIGHTDM_LANGUAGE (object);
+ LightDMLanguagePrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id) {
+ case PROP_CODE:
+ g_free (priv->name);
+ priv->code = g_strdup (g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+lightdm_language_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ LightDMLanguage *self;
+
+ self = LIGHTDM_LANGUAGE (object);
+
+ switch (prop_id) {
+ case PROP_CODE:
+ g_value_set_string (value, lightdm_language_get_code (self));
+ break;
+ case PROP_NAME:
+ g_value_set_string (value, lightdm_language_get_name (self));
+ break;
+ case PROP_TERRITORY:
+ g_value_set_string (value, lightdm_language_get_territory (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+lightdm_language_class_init (LightDMLanguageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (LightDMLanguagePrivate));
+
+ object_class->set_property = lightdm_language_set_property;
+ object_class->get_property = lightdm_language_get_property;
+
+ g_object_class_install_property (object_class,
+ PROP_CODE,
+ g_param_spec_string ("code",
+ "code",
+ "Language code",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class,
+ PROP_NAME,
+ g_param_spec_string ("name",
+ "name",
+ "Name of the language",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_TERRITORY,
+ g_param_spec_string ("territory",
+ "territory",
+ "Territory the language is from",
+ NULL,
+ G_PARAM_READABLE));
+}
diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/layout.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/layout.c
new file mode 100644
index 00000000..d5057450
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/layout.c
@@ -0,0 +1,344 @@
+/* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*-
+ *
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+/*
+ * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
+ * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
+ * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
+ * a choice of LGPL license versions is made available with the language indicating
+ * that LGPLv2 or any later version may be used, or where a choice of which version
+ * of the LGPL is applied is otherwise unspecified.
+ */
+
+#include <libxklavier/xklavier.h>
+
+#include "lightdm/layout.h"
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ PROP_SHORT_DESCRIPTION,
+ PROP_DESCRIPTION
+};
+
+typedef struct
+{
+ gchar *name;
+ gchar *short_description;
+ gchar *description;
+} LightDMLayoutPrivate;
+
+G_DEFINE_TYPE (LightDMLayout, lightdm_layout, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_LAYOUT, LightDMLayoutPrivate)
+
+static gboolean have_layouts = FALSE;
+static Display *display = NULL;
+static XklEngine *xkl_engine = NULL;
+static XklConfigRec *xkl_config = NULL;
+static GList *layouts = NULL;
+static LightDMLayout *default_layout = NULL;
+
+static gchar *
+make_layout_string (const gchar *layout, const gchar *variant)
+{
+ if (!layout || layout[0] == 0)
+ return NULL;
+ else if (!variant || variant[0] == 0)
+ return g_strdup (layout);
+ else
+ return g_strdup_printf ("%s\t%s", layout, variant);
+}
+
+static void
+parse_layout_string (const gchar *name, gchar **layout, gchar **variant)
+{
+ gchar **split;
+
+ *layout = NULL;
+ *variant = NULL;
+
+ if (!name)
+ return;
+
+ split = g_strsplit (name, "\t", 2);
+ if (split[0])
+ {
+ *layout = g_strdup (split[0]);
+ if (split[1])
+ *variant = g_strdup (split[1]);
+ }
+ g_strfreev (split);
+}
+
+static void
+variant_cb (XklConfigRegistry *config,
+ const XklConfigItem *item,
+ gpointer data)
+{
+ LightDMLayout *layout;
+ gchar *full_name;
+
+ full_name = make_layout_string (data, item->name);
+
+ layout = g_object_new (LIGHTDM_TYPE_LAYOUT, "name", full_name, "short-description", item->short_description, "description", item->description, NULL);
+ layouts = g_list_append (layouts, layout);
+
+ g_free (full_name);
+}
+
+static void
+layout_cb (XklConfigRegistry *config,
+ const XklConfigItem *item,
+ gpointer data)
+{
+ LightDMLayout *layout;
+
+ layout = g_object_new (LIGHTDM_TYPE_LAYOUT, "name", item->name, "short-description", item->short_description, "description", item->description, NULL);
+ layouts = g_list_append (layouts, layout);
+
+ xkl_config_registry_foreach_layout_variant (config, item->name, variant_cb, (gpointer) item->name);
+}
+
+/**
+ * lightdm_get_layouts:
+ *
+ * Get a list of keyboard layouts to present to the user.
+ *
+ * Return value: (element-type LightDMLayout) (transfer none): A list of #LightDMLayout that should be presented to the user.
+ **/
+GList *
+lightdm_get_layouts (void)
+{
+ XklConfigRegistry *registry;
+
+ if (have_layouts)
+ return layouts;
+
+ display = XOpenDisplay (NULL);
+ xkl_engine = xkl_engine_get_instance (display);
+ xkl_config = xkl_config_rec_new ();
+ if (!xkl_config_rec_get_from_server (xkl_config, xkl_engine))
+ g_warning ("Failed to get Xkl configuration from server");
+
+ registry = xkl_config_registry_get_instance (xkl_engine);
+ xkl_config_registry_load (registry, FALSE);
+ xkl_config_registry_foreach_layout (registry, layout_cb, NULL);
+ g_object_unref (registry);
+
+ have_layouts = TRUE;
+
+ return layouts;
+}
+
+/**
+ * lightdm_set_layout:
+ * @layout: The layout to use
+ *
+ * Set the layout for this session.
+ **/
+void
+lightdm_set_layout (LightDMLayout *dmlayout)
+{
+ XklConfigRec *config;
+ gchar *layout, *variant;
+
+ g_return_if_fail (dmlayout != NULL);
+
+ g_debug ("Setting keyboard layout to '%s'", lightdm_layout_get_name (dmlayout));
+
+ parse_layout_string (lightdm_layout_get_name (dmlayout), &layout, &variant);
+
+ config = xkl_config_rec_new ();
+ config->layouts = g_malloc (sizeof (gchar *) * 2);
+ config->variants = g_malloc (sizeof (gchar *) * 2);
+ config->model = g_strdup (xkl_config->model);
+ config->layouts[0] = layout;
+ config->layouts[1] = NULL;
+ config->variants[0] = variant;
+ config->variants[1] = NULL;
+ if (!xkl_config_rec_activate (config, xkl_engine))
+ g_warning ("Failed to activate XKL config");
+ g_object_unref (config);
+}
+
+/**
+ * lightdm_get_layout:
+ *
+ * Get the current keyboard layout.
+ *
+ * Return value: (transfer none): The currently active layout for this user.
+ **/
+LightDMLayout *
+lightdm_get_layout (void)
+{
+ lightdm_get_layouts ();
+
+ if (layouts && xkl_config && !default_layout)
+ {
+ gchar *full_name;
+ GList *item;
+
+ full_name = make_layout_string (xkl_config->layouts ? xkl_config->layouts[0] : NULL,
+ xkl_config->variants ? xkl_config->variants[0] : NULL);
+
+ for (item = layouts; item; item = item->next)
+ {
+ LightDMLayout *iter_layout = (LightDMLayout *) item->data;
+ if (g_strcmp0 (lightdm_layout_get_name (iter_layout), full_name) == 0)
+ {
+ default_layout = iter_layout;
+ break;
+ }
+ }
+
+ g_free (full_name);
+ }
+
+ return default_layout;
+}
+
+/**
+ * lightdm_layout_get_name:
+ * @layout: A #LightDMLayout
+ *
+ * Get the name of a layout.
+ *
+ * Return value: The name of the layout
+ **/
+const gchar *
+lightdm_layout_get_name (LightDMLayout *layout)
+{
+ g_return_val_if_fail (LIGHTDM_IS_LAYOUT (layout), NULL);
+ return GET_PRIVATE (layout)->name;
+}
+
+/**
+ * lightdm_layout_get_short_description:
+ * @layout: A #LightDMLayout
+ *
+ * Get the short description of a layout.
+ *
+ * Return value: A short description of the layout
+ **/
+const gchar *
+lightdm_layout_get_short_description (LightDMLayout *layout)
+{
+ g_return_val_if_fail (LIGHTDM_IS_LAYOUT (layout), NULL);
+ return GET_PRIVATE (layout)->short_description;
+}
+
+/**
+ * lightdm_layout_get_description:
+ * @layout: A #LightDMLayout
+ *
+ * Get the long description of a layout.
+ *
+ * Return value: A long description of the layout
+ **/
+const gchar *
+lightdm_layout_get_description (LightDMLayout *layout)
+{
+ g_return_val_if_fail (LIGHTDM_IS_LAYOUT (layout), NULL);
+ return GET_PRIVATE (layout)->description;
+}
+
+static void
+lightdm_layout_init (LightDMLayout *layout)
+{
+}
+
+static void
+lightdm_layout_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ LightDMLayout *self = LIGHTDM_LAYOUT (object);
+ LightDMLayoutPrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_free (priv->name);
+ priv->name = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_SHORT_DESCRIPTION:
+ g_free (priv->short_description);
+ priv->short_description = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_DESCRIPTION:
+ g_free (priv->description);
+ priv->description = g_strdup (g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+lightdm_layout_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ LightDMLayout *self;
+
+ self = LIGHTDM_LAYOUT (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, lightdm_layout_get_name (self));
+ break;
+ case PROP_SHORT_DESCRIPTION:
+ g_value_set_string (value, lightdm_layout_get_short_description (self));
+ break;
+ case PROP_DESCRIPTION:
+ g_value_set_string (value, lightdm_layout_get_description (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+lightdm_layout_class_init (LightDMLayoutClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (LightDMLayoutPrivate));
+
+ object_class->set_property = lightdm_layout_set_property;
+ object_class->get_property = lightdm_layout_get_property;
+
+ g_object_class_install_property (object_class,
+ PROP_NAME,
+ g_param_spec_string ("name",
+ "name",
+ "Name of the layout",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class,
+ PROP_SHORT_DESCRIPTION,
+ g_param_spec_string ("short-description",
+ "short-description",
+ "Short description of the layout",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class,
+ PROP_DESCRIPTION,
+ g_param_spec_string ("description",
+ "description",
+ "Long description of the layout",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/power.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/power.c
new file mode 100644
index 00000000..f7830ff2
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/power.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2010-2011 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+/*
+ * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
+ * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
+ * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
+ * a choice of LGPL license versions is made available with the language indicating
+ * that LGPLv2 or any later version may be used, or where a choice of which version
+ * of the LGPL is applied is otherwise unspecified.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <gio/gio.h>
+
+#include "lightdm/power.h"
+
+static GDBusProxy *upower_proxy = NULL;
+static GDBusProxy *ck_proxy = NULL;
+
+static gboolean
+upower_call_function (const gchar *function, gboolean default_result, GError **error)
+{
+ GVariant *result;
+ gboolean function_result = FALSE;
+
+ if (!upower_proxy)
+ {
+ upower_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.UPower",
+ "/org/freedesktop/UPower",
+ "org.freedesktop.UPower",
+ NULL,
+ error);
+ if (!upower_proxy)
+ return FALSE;
+ }
+
+ result = g_dbus_proxy_call_sync (upower_proxy,
+ function,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ error);
+ if (!result)
+ return default_result;
+
+ if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(b)")))
+ g_variant_get (result, "(b)", &function_result);
+
+ g_variant_unref (result);
+ return function_result;
+}
+
+/**
+ * lightdm_get_can_suspend:
+ *
+ * Checks if authorized to do a system suspend.
+ *
+ * Return value: #TRUE if can suspend the system
+ **/
+gboolean
+lightdm_get_can_suspend (void)
+{
+ return upower_call_function ("SuspendAllowed", FALSE, NULL);
+}
+
+/**
+ * lightdm_suspend:
+ * @error: return location for a #GError, or %NULL
+ *
+ * Triggers a system suspend.
+ *
+ * Return value: #TRUE if suspend initiated.
+ **/
+gboolean
+lightdm_suspend (GError **error)
+{
+ return upower_call_function ("Suspend", TRUE, error);
+}
+
+/**
+ * lightdm_get_can_hibernate:
+ *
+ * Checks if is authorized to do a system hibernate.
+ *
+ * Return value: #TRUE if can hibernate the system
+ **/
+gboolean
+lightdm_get_can_hibernate (void)
+{
+ return upower_call_function ("HibernateAllowed", FALSE, NULL);
+}
+
+/**
+ * lightdm_hibernate:
+ * @error: return location for a #GError, or %NULL
+ *
+ * Triggers a system hibernate.
+ *
+ * Return value: #TRUE if hibernate initiated.
+ **/
+gboolean
+lightdm_hibernate (GError **error)
+{
+ return upower_call_function ("Hibernate", TRUE, error);
+}
+
+static gboolean
+ck_call_function (const gchar *function, gboolean default_result, GError **error)
+{
+ GVariant *result;
+ gboolean function_result = FALSE;
+
+ if (!ck_proxy)
+ {
+ ck_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.ConsoleKit",
+ "/org/freedesktop/ConsoleKit/Manager",
+ "org.freedesktop.ConsoleKit.Manager",
+ NULL,
+ error);
+ if (!ck_proxy)
+ return FALSE;
+ }
+
+ result = g_dbus_proxy_call_sync (ck_proxy,
+ function,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ error);
+
+ if (!result)
+ return default_result;
+
+ if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(b)")))
+ g_variant_get (result, "(b)", &function_result);
+
+ g_variant_unref (result);
+ return function_result;
+}
+
+/**
+ * lightdm_get_can_restart:
+ *
+ * Checks if is authorized to do a system restart.
+ *
+ * Return value: #TRUE if can restart the system
+ **/
+gboolean
+lightdm_get_can_restart (void)
+{
+ return ck_call_function ("CanRestart", FALSE, NULL);
+}
+
+/**
+ * lightdm_restart:
+ * @error: return location for a #GError, or %NULL
+ *
+ * Triggers a system restart.
+ *
+ * Return value: #TRUE if restart initiated.
+ **/
+gboolean
+lightdm_restart (GError **error)
+{
+ return ck_call_function ("Restart", TRUE, error);
+}
+
+/**
+ * lightdm_get_can_shutdown:
+ *
+ * Checks if is authorized to do a system shutdown.
+ *
+ * Return value: #TRUE if can shutdown the system
+ **/
+gboolean
+lightdm_get_can_shutdown (void)
+{
+ return ck_call_function ("CanStop", FALSE, NULL);
+}
+
+/**
+ * lightdm_shutdown:
+ * @error: return location for a #GError, or %NULL
+ *
+ * Triggers a system shutdown.
+ *
+ * Return value: #TRUE if shutdown initiated.
+ **/
+gboolean
+lightdm_shutdown (GError **error)
+{
+ return ck_call_function ("Stop", TRUE, error);
+}
diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/session.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/session.c
new file mode 100644
index 00000000..2df3a1af
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/session.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+/*
+ * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
+ * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
+ * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
+ * a choice of LGPL license versions is made available with the language indicating
+ * that LGPLv2 or any later version may be used, or where a choice of which version
+ * of the LGPL is applied is otherwise unspecified.
+ */
+
+#include <string.h>
+#include <gio/gdesktopappinfo.h>
+
+#include "lightdm/session.h"
+
+enum {
+ PROP_0,
+ PROP_KEY,
+ PROP_NAME,
+ PROP_COMMENT
+};
+
+typedef struct
+{
+ gchar *key;
+ gchar *name;
+ gchar *comment;
+} LightDMSessionPrivate;
+
+G_DEFINE_TYPE (LightDMSession, lightdm_session, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_SESSION, LightDMSessionPrivate)
+
+static gboolean have_sessions = FALSE;
+static GList *local_sessions = NULL;
+static GList *remote_sessions = NULL;
+
+static gint
+compare_session (gconstpointer a, gconstpointer b)
+{
+ LightDMSessionPrivate *priv_a = GET_PRIVATE (a);
+ LightDMSessionPrivate *priv_b = GET_PRIVATE (b);
+ return strcmp (priv_a->name, priv_b->name);
+}
+
+static LightDMSession *
+load_session (GKeyFile *key_file, const gchar *key)
+{
+ gchar *domain, *name;
+ LightDMSession *session;
+ LightDMSessionPrivate *priv;
+ gchar *try_exec;
+
+ if (g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) ||
+ g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL))
+ return NULL;
+
+#ifdef G_KEY_FILE_DESKTOP_KEY_GETTEXT_DOMAIN
+ domain = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_GETTEXT_DOMAIN, NULL);
+#else
+ domain = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GNOME-Gettext-Domain", NULL);
+#endif
+ name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, domain, NULL);
+ if (!name)
+ {
+ g_warning ("Ignoring session without name");
+ g_free (domain);
+ return NULL;
+ }
+
+ try_exec = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TRY_EXEC, domain, NULL);
+ if (try_exec)
+ {
+ gchar *full_path;
+
+ full_path = g_find_program_in_path (try_exec);
+ g_free (try_exec);
+
+ if (!full_path)
+ {
+ g_free (name);
+ g_free (domain);
+ return NULL;
+ }
+ g_free (full_path);
+ }
+
+ session = g_object_new (LIGHTDM_TYPE_SESSION, NULL);
+ priv = GET_PRIVATE (session);
+
+ g_free (priv->key);
+ priv->key = g_strdup (key);
+
+ g_free (priv->name);
+ priv->name = name;
+
+ g_free (priv->comment);
+ priv->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, domain, NULL);
+ if (!priv->comment)
+ priv->comment = g_strdup ("");
+
+ g_free (domain);
+
+ return session;
+}
+
+static GList *
+load_sessions (const gchar *sessions_dir)
+{
+ GDir *directory;
+ GList *sessions = NULL;
+ GError *error = NULL;
+
+ directory = g_dir_open (sessions_dir, 0, &error);
+ if (error)
+ g_warning ("Failed to open sessions directory: %s", error->message);
+ g_clear_error (&error);
+ if (!directory)
+ return NULL;
+
+ while (TRUE)
+ {
+ const gchar *filename;
+ gchar *path;
+ GKeyFile *key_file;
+ gboolean result;
+
+ filename = g_dir_read_name (directory);
+ if (filename == NULL)
+ break;
+
+ if (!g_str_has_suffix (filename, ".desktop"))
+ continue;
+
+ path = g_build_filename (sessions_dir, filename, NULL);
+
+ key_file = g_key_file_new ();
+ result = g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, &error);
+ if (error)
+ g_warning ("Failed to load session file %s: %s:", path, error->message);
+ g_clear_error (&error);
+
+ if (result)
+ {
+ gchar *key;
+ LightDMSession *session;
+
+ key = g_strndup (filename, strlen (filename) - strlen (".desktop"));
+ session = load_session (key_file, key);
+ if (session)
+ {
+ g_debug ("Loaded session %s (%s, %s)", path, GET_PRIVATE (session)->name, GET_PRIVATE (session)->comment);
+ sessions = g_list_insert_sorted (sessions, session, compare_session);
+ }
+ else
+ g_debug ("Ignoring session %s", path);
+ g_free (key);
+ }
+
+ g_free (path);
+ g_key_file_free (key_file);
+ }
+
+ g_dir_close (directory);
+
+ return sessions;
+}
+
+static void
+update_sessions (void)
+{
+ GKeyFile *config_key_file = NULL;
+ gchar *config_path = NULL;
+ gchar *xsessions_dir;
+ gchar *remote_sessions_dir;
+ gboolean result;
+ GError *error = NULL;
+
+ if (have_sessions)
+ return;
+
+ xsessions_dir = g_strdup (XSESSIONS_DIR);
+ remote_sessions_dir = g_strdup (REMOTE_SESSIONS_DIR);
+
+ /* Use session directory from configuration */
+ /* FIXME: This should be sent in the greeter connection */
+ config_path = g_build_filename (CONFIG_DIR, "lightdm.conf", NULL);
+ config_key_file = g_key_file_new ();
+ result = g_key_file_load_from_file (config_key_file, config_path, G_KEY_FILE_NONE, &error);
+ if (error)
+ g_warning ("Failed to open configuration file: %s", error->message);
+ g_clear_error (&error);
+ if (result)
+ {
+ gchar *value;
+
+ value = g_key_file_get_string (config_key_file, "LightDM", "xsessions-directory", NULL);
+ if (value)
+ {
+ g_free (xsessions_dir);
+ xsessions_dir = value;
+ }
+
+ value = g_key_file_get_string (config_key_file, "LightDM", "remote-sessions-directory", NULL);
+ if (value)
+ {
+ g_free (remote_sessions_dir);
+ remote_sessions_dir = value;
+ }
+ }
+ g_key_file_free (config_key_file);
+ g_free (config_path);
+
+ local_sessions = load_sessions (xsessions_dir);
+ remote_sessions = load_sessions (remote_sessions_dir);
+
+ g_free (xsessions_dir);
+ g_free (remote_sessions_dir);
+
+ have_sessions = TRUE;
+}
+
+/**
+ * lightdm_get_sessions:
+ *
+ * Get the available sessions.
+ *
+ * Return value: (element-type LightDMSession) (transfer none): A list of #LightDMSession
+ **/
+GList *
+lightdm_get_sessions (void)
+{
+ update_sessions ();
+ return local_sessions;
+}
+
+/**
+ * lightdm_get_remote_sessions:
+ *
+ * Get the available remote sessions.
+ *
+ * Return value: (element-type LightDMSession) (transfer none): A list of #LightDMSession
+ **/
+GList *
+lightdm_get_remote_sessions (void)
+{
+ update_sessions ();
+ return remote_sessions;
+}
+
+/**
+ * lightdm_session_get_key:
+ * @session: A #LightDMSession
+ *
+ * Get the key for a session
+ *
+ * Return value: The session key
+ **/
+const gchar *
+lightdm_session_get_key (LightDMSession *session)
+{
+ g_return_val_if_fail (LIGHTDM_IS_SESSION (session), NULL);
+ return GET_PRIVATE (session)->key;
+}
+
+/**
+ * lightdm_session_get_name:
+ * @session: A #LightDMSession
+ *
+ * Get the name for a session
+ *
+ * Return value: The session name
+ **/
+const gchar *
+lightdm_session_get_name (LightDMSession *session)
+{
+ g_return_val_if_fail (LIGHTDM_IS_SESSION (session), NULL);
+ return GET_PRIVATE (session)->name;
+}
+
+/**
+ * lightdm_session_get_comment:
+ * @session: A #LightDMSession
+ *
+ * Get the comment for a session
+ *
+ * Return value: The session comment
+ **/
+const gchar *
+lightdm_session_get_comment (LightDMSession *session)
+{
+ g_return_val_if_fail (LIGHTDM_IS_SESSION (session), NULL);
+ return GET_PRIVATE (session)->comment;
+}
+
+static void
+lightdm_session_init (LightDMSession *session)
+{
+}
+
+static void
+lightdm_session_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+lightdm_session_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ LightDMSession *self;
+
+ self = LIGHTDM_SESSION (object);
+
+ switch (prop_id) {
+ case PROP_KEY:
+ g_value_set_string (value, lightdm_session_get_key (self));
+ break;
+ case PROP_NAME:
+ g_value_set_string (value, lightdm_session_get_name (self));
+ break;
+ case PROP_COMMENT:
+ g_value_set_string (value, lightdm_session_get_comment (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+lightdm_session_finalize (GObject *object)
+{
+ LightDMSession *self = LIGHTDM_SESSION (object);
+ LightDMSessionPrivate *priv = GET_PRIVATE (self);
+
+ g_free (priv->key);
+ g_free (priv->name);
+ g_free (priv->comment);
+}
+
+static void
+lightdm_session_class_init (LightDMSessionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (LightDMSessionPrivate));
+
+ object_class->set_property = lightdm_session_set_property;
+ object_class->get_property = lightdm_session_get_property;
+ object_class->finalize = lightdm_session_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_KEY,
+ g_param_spec_string ("key",
+ "key",
+ "Session key",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_NAME,
+ g_param_spec_string ("name",
+ "name",
+ "Session name",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_COMMENT,
+ g_param_spec_string ("comment",
+ "comment",
+ "Session comment",
+ NULL,
+ G_PARAM_READABLE));
+}
diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/system.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/system.c
new file mode 100644
index 00000000..77f8a182
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/system.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010-2011 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+/*
+ * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
+ * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
+ * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
+ * a choice of LGPL license versions is made available with the language indicating
+ * that LGPLv2 or any later version may be used, or where a choice of which version
+ * of the LGPL is applied is otherwise unspecified.
+ */
+
+#include <sys/utsname.h>
+
+#include "lightdm/system.h"
+
+static gchar *hostname = NULL;
+
+/**
+ * lightdm_get_hostname:
+ *
+ * Return value: The name of the host we are running on.
+ **/
+const gchar *
+lightdm_get_hostname (void)
+{
+ if (!hostname)
+ {
+ struct utsname info;
+ uname (&info);
+ hostname = g_strdup (info.nodename);
+ }
+
+ return hostname;
+}
diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/user.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/user.c
new file mode 100644
index 00000000..8df19079
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/user.c
@@ -0,0 +1,1655 @@
+/* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*-
+ *
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+/*
+ * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
+ * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
+ * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
+ * a choice of LGPL license versions is made available with the language indicating
+ * that LGPLv2 or any later version may be used, or where a choice of which version
+ * of the LGPL is applied is otherwise unspecified.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <pwd.h>
+#include <gio/gio.h>
+
+#include "lightdm/user.h"
+
+enum
+{
+ LIST_PROP_0,
+ LIST_PROP_NUM_USERS,
+ LIST_PROP_USERS,
+};
+
+enum
+{
+ USER_PROP_0,
+ USER_PROP_NAME,
+ USER_PROP_REAL_NAME,
+ USER_PROP_DISPLAY_NAME,
+ USER_PROP_HOME_DIRECTORY,
+ USER_PROP_IMAGE,
+ USER_PROP_BACKGROUND,
+ USER_PROP_LANGUAGE,
+ USER_PROP_LAYOUT,
+ USER_PROP_LAYOUTS,
+ USER_PROP_SESSION,
+ USER_PROP_LOGGED_IN,
+ USER_PROP_HAS_MESSAGES
+};
+
+enum
+{
+ USER_ADDED,
+ USER_CHANGED,
+ USER_REMOVED,
+ LAST_LIST_SIGNAL
+};
+static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
+
+enum
+{
+ CHANGED,
+ LAST_USER_SIGNAL
+};
+static guint user_signals[LAST_USER_SIGNAL] = { 0 };
+
+typedef struct
+{
+ /* Connection to AccountsService */
+ GDBusProxy *accounts_service_proxy;
+ GList *user_account_objects;
+
+ /* Connection to DisplayManager */
+ GDBusProxy *display_manager_proxy;
+
+ /* File monitor for password file */
+ GFileMonitor *passwd_monitor;
+
+ /* TRUE if have scanned users */
+ gboolean have_users;
+
+ /* List of users */
+ GList *users;
+
+ /* List of sessions */
+ GList *sessions;
+} LightDMUserListPrivate;
+
+typedef struct
+{
+ GDBusProxy *proxy;
+ LightDMUser *user;
+} UserAccountObject;
+
+typedef struct
+{
+ LightDMUserList *user_list;
+
+ gchar *name;
+ gchar *real_name;
+ gchar *home_directory;
+ gchar *image;
+ gchar *background;
+ gboolean has_messages;
+
+ GKeyFile *dmrc_file;
+ gchar *language;
+ gchar **layouts;
+ gchar *session;
+} LightDMUserPrivate;
+
+typedef struct
+{
+ GObject parent_instance;
+ gchar *path;
+ gchar *username;
+} Session;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} SessionClass;
+
+G_DEFINE_TYPE (LightDMUserList, lightdm_user_list, G_TYPE_OBJECT);
+G_DEFINE_TYPE (LightDMUser, lightdm_user, G_TYPE_OBJECT);
+#define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), session_get_type (), Session))
+GType session_get_type (void);
+G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
+
+#define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER_LIST, LightDMUserListPrivate)
+#define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER, LightDMUserPrivate)
+
+#define PASSWD_FILE "/etc/passwd"
+#define USER_CONFIG_FILE "/etc/lightdm/users.conf"
+
+static LightDMUserList *singleton = NULL;
+
+/**
+ * lightdm_user_list_get_instance:
+ *
+ * Get the user list.
+ *
+ * Return value: (transfer none): the #LightDMUserList
+ **/
+LightDMUserList *
+lightdm_user_list_get_instance (void)
+{
+ if (!singleton)
+ singleton = g_object_new (LIGHTDM_TYPE_USER_LIST, NULL);
+ return singleton;
+}
+
+static LightDMUser *
+get_user_by_name (LightDMUserList *user_list, const gchar *username)
+{
+ LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+ GList *link;
+
+ for (link = priv->users; link; link = link->next)
+ {
+ LightDMUser *user = link->data;
+ if (strcmp (lightdm_user_get_name (user), username) == 0)
+ return user;
+ }
+
+ return NULL;
+}
+
+static gint
+compare_user (gconstpointer a, gconstpointer b)
+{
+ LightDMUser *user_a = (LightDMUser *) a, *user_b = (LightDMUser *) b;
+ return strcmp (lightdm_user_get_display_name (user_a), lightdm_user_get_display_name (user_b));
+}
+
+static gboolean
+update_passwd_user (LightDMUser *user, const gchar *real_name, const gchar *home_directory, const gchar *image)
+{
+ LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
+
+ if (g_strcmp0 (lightdm_user_get_real_name (user), real_name) == 0 &&
+ g_strcmp0 (lightdm_user_get_home_directory (user), home_directory) == 0 &&
+ g_strcmp0 (lightdm_user_get_image (user), image) == 0)
+ return FALSE;
+
+ g_free (priv->real_name);
+ priv->real_name = g_strdup (real_name);
+ g_free (priv->home_directory);
+ priv->home_directory = g_strdup (home_directory);
+ g_free (priv->image);
+ priv->image = g_strdup (image);
+
+ return TRUE;
+}
+
+static void
+user_changed_cb (LightDMUser *user, LightDMUserList *user_list)
+{
+ g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user);
+}
+
+static void
+load_passwd_file (LightDMUserList *user_list, gboolean emit_add_signal)
+{
+ LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+ GKeyFile *config;
+ gchar *value;
+ gint minimum_uid;
+ gchar **hidden_users, **hidden_shells;
+ GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
+ GError *error = NULL;
+
+ g_debug ("Loading user config from %s", USER_CONFIG_FILE);
+
+ config = g_key_file_new ();
+ g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
+ if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+ g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message); // FIXME: Don't make warning on no file, just info
+ g_clear_error (&error);
+
+ if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
+ minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
+ else
+ minimum_uid = 500;
+
+ value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
+ if (!value)
+ value = g_strdup ("nobody nobody4 noaccess");
+ hidden_users = g_strsplit (value, " ", -1);
+ g_free (value);
+
+ value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
+ if (!value)
+ value = g_strdup ("/bin/false /usr/sbin/nologin");
+ hidden_shells = g_strsplit (value, " ", -1);
+ g_free (value);
+
+ g_key_file_free (config);
+
+ setpwent ();
+
+ while (TRUE)
+ {
+ struct passwd *entry;
+ LightDMUser *user;
+ LightDMUserPrivate *user_priv;
+ char **tokens;
+ gchar *real_name, *image;
+ int i;
+
+ errno = 0;
+ entry = getpwent ();
+ if (!entry)
+ break;
+
+ /* Ignore system users */
+ if (entry->pw_uid < minimum_uid)
+ continue;
+
+ /* Ignore users disabled by shell */
+ if (entry->pw_shell)
+ {
+ for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
+ if (hidden_shells[i])
+ continue;
+ }
+
+ /* Ignore certain users */
+ for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
+ if (hidden_users[i])
+ continue;
+
+ tokens = g_strsplit (entry->pw_gecos, ",", -1);
+ if (tokens[0] != NULL && tokens[0][0] != '\0')
+ real_name = g_strdup (tokens[0]);
+ else
+ real_name = g_strdup ("");
+ g_strfreev (tokens);
+
+ image = g_build_filename (entry->pw_dir, ".face", NULL);
+ if (!g_file_test (image, G_FILE_TEST_EXISTS))
+ {
+ g_free (image);
+ image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
+ if (!g_file_test (image, G_FILE_TEST_EXISTS))
+ {
+ g_free (image);
+ image = NULL;
+ }
+ }
+
+ user = g_object_new (LIGHTDM_TYPE_USER, NULL);
+ user_priv = GET_USER_PRIVATE (user);
+ user_priv->user_list = user_list;
+ g_free (user_priv->name);
+ user_priv->name = g_strdup (entry->pw_name);
+ g_free (user_priv->real_name);
+ user_priv->real_name = real_name;
+ g_free (user_priv->home_directory);
+ user_priv->home_directory = g_strdup (entry->pw_dir);
+ g_free (user_priv->image);
+ user_priv->image = image;
+
+ /* Update existing users if have them */
+ for (link = priv->users; link; link = link->next)
+ {
+ LightDMUser *info = link->data;
+ if (strcmp (lightdm_user_get_name (info), lightdm_user_get_name (user)) == 0)
+ {
+ if (update_passwd_user (info, lightdm_user_get_real_name (user), lightdm_user_get_home_directory (user), lightdm_user_get_image (user)))
+ changed_users = g_list_insert_sorted (changed_users, info, compare_user);
+ g_object_unref (user);
+ user = info;
+ break;
+ }
+ }
+ if (!link)
+ {
+ /* Only notify once we have loaded the user list */
+ if (priv->have_users)
+ new_users = g_list_insert_sorted (new_users, user, compare_user);
+ }
+ users = g_list_insert_sorted (users, user, compare_user);
+ }
+ g_strfreev (hidden_users);
+ g_strfreev (hidden_shells);
+
+ if (errno != 0)
+ g_warning ("Failed to read password database: %s", strerror (errno));
+
+ endpwent ();
+
+ /* Use new user list */
+ old_users = priv->users;
+ priv->users = users;
+
+ /* Notify of changes */
+ for (link = new_users; link; link = link->next)
+ {
+ LightDMUser *info = link->data;
+ g_debug ("User %s added", lightdm_user_get_name (info));
+ g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), user_list);
+ if (emit_add_signal)
+ g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
+ }
+ g_list_free (new_users);
+ for (link = changed_users; link; link = link->next)
+ {
+ LightDMUser *info = link->data;
+ g_debug ("User %s changed", lightdm_user_get_name (info));
+ g_signal_emit (info, user_signals[CHANGED], 0);
+ }
+ g_list_free (changed_users);
+ for (link = old_users; link; link = link->next)
+ {
+ GList *new_link;
+
+ /* See if this user is in the current list */
+ for (new_link = priv->users; new_link; new_link = new_link->next)
+ {
+ if (new_link->data == link->data)
+ break;
+ }
+
+ if (!new_link)
+ {
+ LightDMUser *info = link->data;
+ g_debug ("User %s removed", lightdm_user_get_name (info));
+ g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
+ g_object_unref (info);
+ }
+ }
+ g_list_free (old_users);
+}
+
+static void
+passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, LightDMUserList *user_list)
+{
+ if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+ {
+ g_debug ("%s changed, reloading user list", g_file_get_path (file));
+ load_passwd_file (user_list, TRUE);
+ }
+}
+
+static gboolean
+update_user (UserAccountObject *object)
+{
+ LightDMUserPrivate *priv = GET_USER_PRIVATE (object->user);
+ GVariant *result, *value;
+ GVariantIter *iter;
+ gchar *name;
+ GError *error = NULL;
+
+ result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (object->proxy),
+ "org.freedesktop.Accounts",
+ g_dbus_proxy_get_object_path (object->proxy),
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
+ G_VARIANT_TYPE ("(a{sv})"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (error)
+ g_warning ("Error updating user %s: %s", g_dbus_proxy_get_object_path (object->proxy), error->message);
+ g_clear_error (&error);
+ if (!result)
+ return FALSE;
+
+ g_variant_get (result, "(a{sv})", &iter);
+ while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
+ {
+ if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+ {
+ gchar *user_name;
+ g_variant_get (value, "&s", &user_name);
+ g_free (priv->name);
+ priv->name = g_strdup (user_name);
+ }
+ else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+ {
+ gchar *real_name;
+ g_variant_get (value, "&s", &real_name);
+ g_free (priv->real_name);
+ priv->real_name = g_strdup (real_name);
+ }
+ else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+ {
+ gchar *home_directory;
+ g_variant_get (value, "&s", &home_directory);
+ g_free (priv->home_directory);
+ priv->home_directory = g_strdup (home_directory);
+ }
+ else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+ {
+ gchar *icon_file;
+ g_variant_get (value, "&s", &icon_file);
+ g_free (priv->image);
+ if (strcmp (icon_file, "") == 0)
+ priv->image = NULL;
+ else
+ priv->image = g_strdup (icon_file);
+ }
+ else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+ {
+ gchar *background_file;
+ g_variant_get (value, "&s", &background_file);
+ g_free (priv->background);
+ if (strcmp (background_file, "") == 0)
+ priv->background = NULL;
+ else
+ priv->background = g_strdup (background_file);
+ }
+ }
+ g_variant_iter_free (iter);
+
+ g_variant_unref (result);
+
+ return TRUE;
+}
+
+static void
+user_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, UserAccountObject *object)
+{
+ if (strcmp (signal_name, "Changed") == 0)
+ {
+ if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()")))
+ {
+ g_debug ("User %s changed", g_dbus_proxy_get_object_path (object->proxy));
+ update_user (object);
+ g_signal_emit (object->user, user_signals[CHANGED], 0);
+ }
+ else
+ g_warning ("Got org.freedesktop.Accounts.User signal Changed with unknown parameters %s", g_variant_get_type_string (parameters));
+ }
+}
+
+static UserAccountObject *
+user_account_object_new (LightDMUserList *user_list, const gchar *path)
+{
+ GDBusProxy *proxy;
+ UserAccountObject *object;
+ GError *error = NULL;
+
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.Accounts",
+ path,
+ "org.freedesktop.Accounts.User",
+ NULL,
+ &error);
+ if (error)
+ g_warning ("Error getting user %s: %s", path, error->message);
+ g_clear_error (&error);
+ if (!proxy)
+ return NULL;
+
+ object = g_malloc0 (sizeof (UserAccountObject));
+ object->user = g_object_new (LIGHTDM_TYPE_USER, NULL);
+ GET_USER_PRIVATE (object->user)->user_list = user_list;
+ object->proxy = proxy;
+ g_signal_connect (proxy, "g-signal", G_CALLBACK (user_signal_cb), object);
+
+ return object;
+}
+
+static void
+user_account_object_free (UserAccountObject *object)
+{
+ if (!object)
+ return;
+ g_object_unref (object->user);
+ g_object_unref (object->proxy);
+ g_free (object);
+}
+
+static UserAccountObject *
+find_user_account_object (LightDMUserList *user_list, const gchar *path)
+{
+ LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+ GList *link;
+
+ for (link = priv->user_account_objects; link; link = link->next)
+ {
+ UserAccountObject *object = link->data;
+ if (strcmp (g_dbus_proxy_get_object_path (object->proxy), path) == 0)
+ return object;
+ }
+
+ return NULL;
+}
+
+static void
+user_accounts_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
+{
+ LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+
+ if (strcmp (signal_name, "UserAdded") == 0)
+ {
+ if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
+ {
+ gchar *path;
+ UserAccountObject *object;
+
+ g_variant_get (parameters, "(&o)", &path);
+
+ /* Ignore duplicate requests */
+ object = find_user_account_object (user_list, path);
+ if (object)
+ return;
+
+ object = user_account_object_new (user_list, path);
+ if (object && update_user (object))
+ {
+ g_debug ("User %s added", path);
+ priv->user_account_objects = g_list_append (priv->user_account_objects, object);
+ priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
+ g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
+ g_signal_emit (user_list, list_signals[USER_ADDED], 0, object->user);
+ }
+ else
+ user_account_object_free (object);
+ }
+ else
+ g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
+ }
+ else if (strcmp (signal_name, "UserDeleted") == 0)
+ {
+ if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
+ {
+ gchar *path;
+ UserAccountObject *object;
+
+ g_variant_get (parameters, "(&o)", &path);
+
+ object = find_user_account_object (user_list, path);
+ if (!object)
+ return;
+
+ g_debug ("User %s deleted", path);
+ priv->users = g_list_remove (priv->users, object->user);
+ g_object_unref (object->user);
+
+ g_signal_emit (user_list, list_signals[USER_REMOVED], 0, object->user);
+
+ priv->user_account_objects = g_list_remove (priv->user_account_objects, object);
+ user_account_object_free (object);
+ }
+ else
+ g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
+ }
+}
+
+static Session *
+load_session (LightDMUserList *user_list, const gchar *path)
+{
+ LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+ Session *session = NULL;
+ GVariant *result, *username;
+ GError *error = NULL;
+
+ result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
+ "org.freedesktop.DisplayManager",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
+ G_VARIANT_TYPE ("(v)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (error)
+ g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
+ g_clear_error (&error);
+ if (!result)
+ return NULL;
+
+ g_variant_get (result, "(v)", &username);
+ if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
+ {
+ gchar *name;
+
+ g_variant_get (username, "&s", &name);
+
+ g_debug ("Loaded session %s (%s)", path, name);
+ session = g_object_new (session_get_type (), NULL);
+ session->username = g_strdup (name);
+ session->path = g_strdup (path);
+ priv->sessions = g_list_append (priv->sessions, session);
+ }
+ g_variant_unref (username);
+ g_variant_unref (result);
+
+ return session;
+}
+
+static void
+display_manager_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
+{
+ LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+
+ if (strcmp (signal_name, "SessionAdded") == 0)
+ {
+ if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
+ {
+ gchar *path;
+ Session *session;
+ LightDMUser *user = NULL;
+
+ g_variant_get (parameters, "(&o)", &path);
+ session = load_session (user_list, path);
+ if (session)
+ user = get_user_by_name (user_list, session->username);
+ if (user)
+ g_signal_emit (user, user_signals[CHANGED], 0);
+ }
+ }
+ else if (strcmp (signal_name, "SessionRemoved") == 0)
+ {
+ if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
+ {
+ gchar *path;
+ GList *link;
+
+ g_variant_get (parameters, "(&o)", &path);
+
+ for (link = priv->sessions; link; link = link->next)
+ {
+ Session *session = link->data;
+ if (strcmp (session->path, path) == 0)
+ {
+ LightDMUser *user;
+
+ g_debug ("Session %s removed", path);
+ priv->sessions = g_list_remove_link (priv->sessions, link);
+ user = get_user_by_name (user_list, session->username);
+ if (user)
+ g_signal_emit (user, user_signals[CHANGED], 0);
+ g_object_unref (session);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void
+update_users (LightDMUserList *user_list)
+{
+ LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+ GError *error = NULL;
+
+ if (priv->have_users)
+ return;
+ priv->have_users = TRUE;
+
+ priv->accounts_service_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.Accounts",
+ "/org/freedesktop/Accounts",
+ "org.freedesktop.Accounts",
+ NULL,
+ &error);
+ if (error)
+ g_warning ("Error contacting org.freedesktop.Accounts: %s", error->message);
+ g_clear_error (&error);
+
+ /* Check if the service exists */
+ if (priv->accounts_service_proxy)
+ {
+ gchar *name;
+
+ name = g_dbus_proxy_get_name_owner (priv->accounts_service_proxy);
+ if (!name)
+ {
+ g_debug ("org.freedesktop.Accounts does not exist, falling back to passwd file");
+ g_object_unref (priv->accounts_service_proxy);
+ priv->accounts_service_proxy = NULL;
+ }
+ g_free (name);
+ }
+
+ if (priv->accounts_service_proxy)
+ {
+ GVariant *result;
+
+ g_signal_connect (priv->accounts_service_proxy, "g-signal", G_CALLBACK (user_accounts_signal_cb), user_list);
+
+ result = g_dbus_proxy_call_sync (priv->accounts_service_proxy,
+ "ListCachedUsers",
+ g_variant_new ("()"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (error)
+ g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
+ g_clear_error (&error);
+ if (!result)
+ return;
+
+ if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(ao)")))
+ {
+ GVariantIter *iter;
+ const gchar *path;
+
+ g_debug ("Loading users from org.freedesktop.Accounts");
+ g_variant_get (result, "(ao)", &iter);
+ while (g_variant_iter_loop (iter, "&o", &path))
+ {
+ UserAccountObject *object;
+
+ g_debug ("Loading user %s", path);
+
+ object = user_account_object_new (user_list, path);
+ if (object && update_user (object))
+ {
+ priv->user_account_objects = g_list_append (priv->user_account_objects, object);
+ priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
+ g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
+ }
+ else
+ user_account_object_free (object);
+ }
+ g_variant_iter_free (iter);
+ }
+ else
+ g_warning ("Unexpected type from ListCachedUsers: %s", g_variant_get_type_string (result));
+
+ g_variant_unref (result);
+ }
+ else
+ {
+ GFile *passwd_file;
+
+ load_passwd_file (user_list, FALSE);
+
+ /* Watch for changes to user list */
+
+ passwd_file = g_file_new_for_path (PASSWD_FILE);
+ priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
+ g_object_unref (passwd_file);
+ if (error)
+ g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
+ else
+ g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
+ g_clear_error (&error);
+ }
+
+ priv->display_manager_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.DisplayManager",
+ "/org/freedesktop/DisplayManager",
+ "org.freedesktop.DisplayManager",
+ NULL,
+ &error);
+ if (error)
+ g_warning ("Error contacting org.freedesktop.DisplayManager: %s", error->message);
+ g_clear_error (&error);
+
+ if (priv->display_manager_proxy)
+ {
+ GVariant *result;
+
+ g_signal_connect (priv->display_manager_proxy, "g-signal", G_CALLBACK (display_manager_signal_cb), user_list);
+
+ result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
+ "org.freedesktop.DisplayManager",
+ "/org/freedesktop/DisplayManager",
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
+ G_VARIANT_TYPE ("(v)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (error)
+ g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
+ g_clear_error (&error);
+ if (!result)
+ return;
+
+ if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
+ {
+ GVariant *value;
+ GVariantIter *iter;
+ const gchar *path;
+
+ g_variant_get (result, "(v)", &value);
+
+ g_debug ("Loading sessions from org.freedesktop.DisplayManager");
+ g_variant_get (value, "ao", &iter);
+ while (g_variant_iter_loop (iter, "&o", &path))
+ load_session (user_list, path);
+ g_variant_iter_free (iter);
+
+ g_variant_unref (value);
+ }
+ else
+ g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
+
+ g_variant_unref (result);
+ }
+}
+
+/**
+ * lightdm_user_list_get_length:
+ * @user_list: a #LightDMUserList
+ *
+ * Return value: The number of users able to log in
+ **/
+gint
+lightdm_user_list_get_length (LightDMUserList *user_list)
+{
+ g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0);
+ update_users (user_list);
+ return g_list_length (GET_LIST_PRIVATE (user_list)->users);
+}
+
+/**
+ * lightdm_user_list_get_users:
+ * @user_list: A #LightDMUserList
+ *
+ * Get a list of users to present to the user. This list may be a subset of the
+ * available users and may be empty depending on the server configuration.
+ *
+ * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user.
+ **/
+GList *
+lightdm_user_list_get_users (LightDMUserList *user_list)
+{
+ g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
+ update_users (user_list);
+ return GET_LIST_PRIVATE (user_list)->users;
+}
+
+/**
+ * lightdm_user_list_get_user_by_name:
+ * @user_list: A #LightDMUserList
+ * @username: Name of user to get.
+ *
+ * Get infomation about a given user or #NULL if this user doesn't exist.
+ *
+ * Return value: (transfer none): A #LightDMUser entry for the given user.
+ **/
+LightDMUser *
+lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username)
+{
+ g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
+ g_return_val_if_fail (username != NULL, NULL);
+
+ update_users (user_list);
+
+ return get_user_by_name (user_list, username);
+}
+
+static void
+lightdm_user_list_init (LightDMUserList *user_list)
+{
+}
+
+static void
+lightdm_user_list_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+lightdm_user_list_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ LightDMUserList *self;
+
+ self = LIGHTDM_USER_LIST (object);
+
+ switch (prop_id)
+ {
+ case LIST_PROP_NUM_USERS:
+ g_value_set_int (value, lightdm_user_list_get_length (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+lightdm_user_list_finalize (GObject *object)
+{
+ LightDMUserList *self = LIGHTDM_USER_LIST (object);
+ LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self);
+
+ if (priv->accounts_service_proxy)
+ g_object_unref (priv->accounts_service_proxy);
+ g_list_free_full (priv->user_account_objects, (GDestroyNotify) user_account_object_free);
+ if (priv->passwd_monitor)
+ g_object_unref (priv->passwd_monitor);
+ g_list_free_full (priv->users, g_object_unref);
+ g_list_free_full (priv->sessions, g_object_unref);
+
+ G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object);
+}
+
+static void
+lightdm_user_list_class_init (LightDMUserListClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (LightDMUserListPrivate));
+
+ object_class->set_property = lightdm_user_list_set_property;
+ object_class->get_property = lightdm_user_list_get_property;
+ object_class->finalize = lightdm_user_list_finalize;
+
+ g_object_class_install_property (object_class,
+ LIST_PROP_NUM_USERS,
+ g_param_spec_int ("num-users",
+ "num-users",
+ "Number of login users",
+ 0, G_MAXINT, 0,
+ G_PARAM_READABLE));
+ /**
+ * LightDMUserList::user-added:
+ * @user_list: A #LightDMUserList
+ * @user: The #LightDM user that has been added.
+ *
+ * The ::user-added signal gets emitted when a user account is created.
+ **/
+ list_signals[USER_ADDED] =
+ g_signal_new ("user-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (LightDMUserListClass, user_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
+
+ /**
+ * LightDMUserList::user-changed:
+ * @user_list: A #LightDMUserList
+ * @user: The #LightDM user that has been changed.
+ *
+ * The ::user-changed signal gets emitted when a user account is modified.
+ **/
+ list_signals[USER_CHANGED] =
+ g_signal_new ("user-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
+
+ /**
+ * LightDMUserList::user-removed:
+ * @user_list: A #LightDMUserList
+ * @user: The #LightDM user that has been removed.
+ *
+ * The ::user-removed signal gets emitted when a user account is removed.
+ **/
+ list_signals[USER_REMOVED] =
+ g_signal_new ("user-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
+}
+
+/**
+ * lightdm_user_get_name:
+ * @user: A #LightDMUser
+ *
+ * Get the name of a user.
+ *
+ * Return value: The name of the given user
+ **/
+const gchar *
+lightdm_user_get_name (LightDMUser *user)
+{
+ g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+ return GET_USER_PRIVATE (user)->name;
+}
+
+/**
+ * lightdm_user_get_real_name:
+ * @user: A #LightDMUser
+ *
+ * Get the real name of a user.
+ *
+ * Return value: The real name of the given user
+ **/
+const gchar *
+lightdm_user_get_real_name (LightDMUser *user)
+{
+ g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+ return GET_USER_PRIVATE (user)->real_name;
+}
+
+/**
+ * lightdm_user_get_display_name:
+ * @user: A #LightDMUser
+ *
+ * Get the display name of a user.
+ *
+ * Return value: The display name of the given user
+ **/
+const gchar *
+lightdm_user_get_display_name (LightDMUser *user)
+{
+ LightDMUserPrivate *priv;
+
+ g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+
+ priv = GET_USER_PRIVATE (user);
+ if (strcmp (priv->real_name, ""))
+ return priv->real_name;
+ else
+ return priv->name;
+}
+
+/**
+ * lightdm_user_get_home_directory:
+ * @user: A #LightDMUser
+ *
+ * Get the home directory for a user.
+ *
+ * Return value: The users home directory
+ */
+const gchar *
+lightdm_user_get_home_directory (LightDMUser *user)
+{
+ g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+ return GET_USER_PRIVATE (user)->home_directory;
+}
+
+/**
+ * lightdm_user_get_image:
+ * @user: A #LightDMUser
+ *
+ * Get the image URI for a user.
+ *
+ * Return value: The image URI for the given user or #NULL if no URI
+ **/
+const gchar *
+lightdm_user_get_image (LightDMUser *user)
+{
+ g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+ return GET_USER_PRIVATE (user)->image;
+}
+
+/**
+ * lightdm_user_get_background:
+ * @user: A #LightDMUser
+ *
+ * Get the background file path for a user.
+ *
+ * Return value: The background file path for the given user or #NULL if no path
+ **/
+const gchar *
+lightdm_user_get_background (LightDMUser *user)
+{
+ g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+ return GET_USER_PRIVATE (user)->background;
+}
+
+static void
+load_dmrc (LightDMUser *user)
+{
+ LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
+ gchar *path;
+ //gboolean have_dmrc;
+
+ if (!priv->dmrc_file)
+ priv->dmrc_file = g_key_file_new ();
+
+ /* Load from the user directory */
+ path = g_build_filename (priv->home_directory, ".dmrc", NULL);
+ /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
+ g_free (path);
+
+ /* If no ~/.dmrc, then load from the cache */
+ // FIXME
+
+ // FIXME: Watch for changes
+
+ /* The Language field is actually a locale, strip the codeset off it to get the language */
+ if (priv->language)
+ g_free (priv->language);
+ priv->language = g_key_file_get_string (priv->dmrc_file, "Desktop", "Language", NULL);
+ if (priv->language)
+ {
+ gchar *codeset = strchr (priv->language, '.');
+ if (codeset)
+ *codeset = '\0';
+ }
+
+ if (priv->layouts)
+ {
+ g_strfreev (priv->layouts);
+ priv->layouts = NULL;
+ }
+ if (g_key_file_has_key (priv->dmrc_file, "Desktop", "Layout", NULL))
+ {
+ priv->layouts = g_malloc (sizeof (gchar *) * 2);
+ priv->layouts[0] = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
+ priv->layouts[1] = NULL;
+ }
+
+ if (priv->session)
+ g_free (priv->session);
+ priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
+}
+
+static GVariant *
+get_property (GDBusProxy *proxy, const gchar *property)
+{
+ GVariant *answer;
+
+ if (!proxy)
+ return NULL;
+
+ answer = g_dbus_proxy_get_cached_property (proxy, property);
+
+ if (!answer)
+ {
+ g_warning ("Could not get accounts property %s", property);
+ return NULL;
+ }
+
+ return answer;
+}
+
+static gboolean
+get_boolean_property (GDBusProxy *proxy, const gchar *property)
+{
+ GVariant *answer;
+ gboolean rv;
+
+ answer = get_property (proxy, property);
+ if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_BOOLEAN))
+ {
+ g_warning ("Unexpected accounts property type for %s: %s",
+ property, g_variant_get_type_string (answer));
+ g_variant_unref (answer);
+ return FALSE;
+ }
+
+ rv = g_variant_get_boolean (answer);
+ g_variant_unref (answer);
+
+ return rv;
+}
+
+static gchar *
+get_string_property (GDBusProxy *proxy, const gchar *property)
+{
+ GVariant *answer;
+ gchar *rv;
+
+ answer = get_property (proxy, property);
+ if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_STRING))
+ {
+ g_warning ("Unexpected accounts property type for %s: %s",
+ property, g_variant_get_type_string (answer));
+ g_variant_unref (answer);
+ return NULL;
+ }
+
+ rv = g_strdup (g_variant_get_string (answer, NULL));
+ if (strcmp (rv, "") == 0)
+ {
+ g_free (rv);
+ rv = NULL;
+ }
+ g_variant_unref (answer);
+
+ return rv;
+}
+
+static gchar **
+get_string_array_property (GDBusProxy *proxy, const gchar *property)
+{
+ GVariant *answer;
+ gchar **rv;
+
+ if (!proxy)
+ return NULL;
+
+ answer = g_dbus_proxy_get_cached_property (proxy, property);
+
+ if (!answer)
+ {
+ g_warning ("Could not get accounts property %s", property);
+ return NULL;
+ }
+
+ if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("as")))
+ {
+ g_warning ("Unexpected accounts property type for %s: %s",
+ property, g_variant_get_type_string (answer));
+ g_variant_unref (answer);
+ return NULL;
+ }
+
+ rv = g_variant_dup_strv (answer, NULL);
+
+ g_variant_unref (answer);
+ return rv;
+}
+
+static gboolean
+load_accounts_service (LightDMUser *user)
+{
+ LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
+ LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
+ UserAccountObject *account = NULL;
+ GList *iter;
+ gchar **value;
+
+ /* First, find AccountObject proxy */
+ for (iter = list_priv->user_account_objects; iter; iter = iter->next)
+ {
+ UserAccountObject *a = iter->data;
+ if (a->user == user)
+ {
+ account = a;
+ break;
+ }
+ }
+ if (!account)
+ return FALSE;
+
+ /* We have proxy, let's grab some properties */
+ if (priv->language)
+ g_free (priv->language);
+ priv->language = get_string_property (account->proxy, "Language");
+ if (priv->session)
+ g_free (priv->session);
+ priv->session = get_string_property (account->proxy, "XSession");
+
+ value = get_string_array_property (account->proxy, "XKeyboardLayouts");
+ if (value)
+ {
+ if (value[0])
+ {
+ g_strfreev (priv->layouts);
+ priv->layouts = value;
+ }
+ else
+ g_strfreev (value);
+ }
+
+ priv->has_messages = get_boolean_property (account->proxy, "XHasMessages");
+
+ return TRUE;
+}
+
+/* Loads language/layout/session info for user */
+static void
+load_user_values (LightDMUser *user)
+{
+ LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
+
+ load_dmrc (user);
+ load_accounts_service (user); // overrides dmrc values
+
+ /* Ensure a few guarantees */
+ if (priv->layouts == NULL)
+ {
+ priv->layouts = g_malloc (sizeof (gchar *) * 1);
+ priv->layouts[0] = NULL;
+ }
+}
+
+/**
+ * lightdm_user_get_language:
+ * @user: A #LightDMUser
+ *
+ * Get the language for a user.
+ *
+ * Return value: The language for the given user or #NULL if using system defaults.
+ **/
+const gchar *
+lightdm_user_get_language (LightDMUser *user)
+{
+ g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+ load_user_values (user);
+ return GET_USER_PRIVATE (user)->language;
+}
+
+/**
+ * lightdm_user_get_layout:
+ * @user: A #LightDMUser
+ *
+ * Get the keyboard layout for a user.
+ *
+ * Return value: The keyboard layout for the given user or #NULL if using system defaults. Copy the value if you want to use it long term.
+ **/
+const gchar *
+lightdm_user_get_layout (LightDMUser *user)
+{
+ g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+ load_user_values (user);
+ return GET_USER_PRIVATE (user)->layouts[0];
+}
+
+/**
+ * lightdm_user_get_layouts:
+ * @user: A #LightDMUser
+ *
+ * Get the configured keyboard layouts for a user.
+ *
+ * Return value: (transfer none): A NULL-terminated array of keyboard layouts for the given user. Copy the values if you want to use them long term.
+ **/
+const gchar * const *
+lightdm_user_get_layouts (LightDMUser *user)
+{
+ g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+ load_user_values (user);
+ return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
+}
+
+/**
+ * lightdm_user_get_session:
+ * @user: A #LightDMUser
+ *
+ * Get the session for a user.
+ *
+ * Return value: The session for the given user or #NULL if using system defaults.
+ **/
+const gchar *
+lightdm_user_get_session (LightDMUser *user)
+{
+ g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+ load_user_values (user);
+ return GET_USER_PRIVATE (user)->session;
+}
+
+/**
+ * lightdm_user_get_logged_in:
+ * @user: A #LightDMUser
+ *
+ * Check if a user is logged in.
+ *
+ * Return value: #TRUE if the user is currently logged in.
+ **/
+gboolean
+lightdm_user_get_logged_in (LightDMUser *user)
+{
+ LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
+ LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
+ GList *link;
+
+ g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
+
+ for (link = list_priv->sessions; link; link = link->next)
+ {
+ Session *session = link->data;
+ if (strcmp (session->username, priv->name) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * lightdm_user_get_has_messages:
+ * @user: A #LightDMUser
+ *
+ * Check if a user has waiting messages.
+ *
+ * Return value: #TRUE if the user has waiting messages.
+ **/
+gboolean
+lightdm_user_get_has_messages (LightDMUser *user)
+{
+ g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
+ load_user_values (user);
+ return GET_USER_PRIVATE (user)->has_messages;
+}
+
+static void
+lightdm_user_init (LightDMUser *user)
+{
+}
+
+static void
+lightdm_user_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+lightdm_user_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ LightDMUser *self;
+
+ self = LIGHTDM_USER (object);
+
+ switch (prop_id)
+ {
+ case USER_PROP_NAME:
+ g_value_set_string (value, lightdm_user_get_name (self));
+ break;
+ case USER_PROP_REAL_NAME:
+ g_value_set_string (value, lightdm_user_get_real_name (self));
+ break;
+ case USER_PROP_DISPLAY_NAME:
+ g_value_set_string (value, lightdm_user_get_display_name (self));
+ break;
+ case USER_PROP_HOME_DIRECTORY:
+ g_value_set_string (value, lightdm_user_get_home_directory (self));
+ break;
+ case USER_PROP_IMAGE:
+ g_value_set_string (value, lightdm_user_get_image (self));
+ break;
+ case USER_PROP_BACKGROUND:
+ g_value_set_string (value, lightdm_user_get_background (self));
+ break;
+ case USER_PROP_LANGUAGE:
+ g_value_set_string (value, lightdm_user_get_language (self));
+ break;
+ case USER_PROP_LAYOUT:
+ g_value_set_string (value, lightdm_user_get_layout (self));
+ break;
+ case USER_PROP_LAYOUTS:
+ g_value_set_boxed (value, g_strdupv ((gchar **) lightdm_user_get_layouts (self)));
+ break;
+ case USER_PROP_SESSION:
+ g_value_set_string (value, lightdm_user_get_session (self));
+ break;
+ case USER_PROP_LOGGED_IN:
+ g_value_set_boolean (value, lightdm_user_get_logged_in (self));
+ break;
+ case USER_PROP_HAS_MESSAGES:
+ g_value_set_boolean (value, lightdm_user_get_has_messages (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+lightdm_user_finalize (GObject *object)
+{
+ LightDMUser *self = LIGHTDM_USER (object);
+ LightDMUserPrivate *priv = GET_USER_PRIVATE (self);
+
+ g_free (priv->name);
+ g_free (priv->real_name);
+ g_free (priv->home_directory);
+ g_free (priv->image);
+ g_free (priv->background);
+ g_strfreev (priv->layouts);
+ if (priv->dmrc_file)
+ g_key_file_free (priv->dmrc_file);
+}
+
+static void
+lightdm_user_class_init (LightDMUserClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (LightDMUserPrivate));
+
+ object_class->set_property = lightdm_user_set_property;
+ object_class->get_property = lightdm_user_get_property;
+ object_class->finalize = lightdm_user_finalize;
+
+ g_object_class_install_property (object_class,
+ USER_PROP_NAME,
+ g_param_spec_string ("name",
+ "name",
+ "Username",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ USER_PROP_REAL_NAME,
+ g_param_spec_string ("real-name",
+ "real-name",
+ "Users real name",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ USER_PROP_DISPLAY_NAME,
+ g_param_spec_string ("display-name",
+ "display-name",
+ "Users display name",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ USER_PROP_HOME_DIRECTORY,
+ g_param_spec_string ("home-directory",
+ "home-directory",
+ "Home directory",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ USER_PROP_IMAGE,
+ g_param_spec_string ("image",
+ "image",
+ "Avatar image",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ USER_PROP_BACKGROUND,
+ g_param_spec_string ("background",
+ "background",
+ "User background",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ USER_PROP_LANGUAGE,
+ g_param_spec_string ("language",
+ "language",
+ "Language used by this user",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ USER_PROP_LAYOUT,
+ g_param_spec_string ("layout",
+ "layout",
+ "Keyboard layout used by this user",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ USER_PROP_LAYOUTS,
+ g_param_spec_boxed ("layouts",
+ "layouts",
+ "Keyboard layouts used by this user",
+ G_TYPE_STRV,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ USER_PROP_SESSION,
+ g_param_spec_string ("session",
+ "session",
+ "Session used by this user",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ USER_PROP_LOGGED_IN,
+ g_param_spec_boolean ("logged-in",
+ "logged-in",
+ "TRUE if the user is currently in a session",
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ USER_PROP_LOGGED_IN,
+ g_param_spec_boolean ("has-messages",
+ "has-messages",
+ "TRUE if the user is has waiting messages",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ /**
+ * LightDMUser::changed:
+ * @user: A #LightDMUser
+ *
+ * The ::changed signal gets emitted this user account is modified.
+ **/
+ user_signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (LightDMUserClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+session_init (Session *session)
+{
+}
+
+static void
+session_finalize (GObject *object)
+{
+ Session *self = SESSION (object);
+
+ g_free (self->path);
+ g_free (self->username);
+}
+
+static void
+session_class_init (SessionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = session_finalize;
+}
diff --git a/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.cpp b/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.cpp
new file mode 100644
index 00000000..a799e0db
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.cpp
@@ -0,0 +1,1520 @@
+/* $Id: vbox-greeter.cpp $ */
+/** @file
+ * vbox-greeter - an own LightDM greeter module supporting auto-logons
+ * controlled by the host.
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define GLIB_DISABLE_DEPRECATION_WARNINGS 1 /* g_type_init() is deprecated */
+#include <pwd.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#include <lightdm.h>
+#ifdef VBOX_WITH_FLTK
+# include <FL/Fl.H>
+# include <FL/fl_ask.H> /* Yes, the casing is correct for 1.3.0 -- d'oh. */
+# include <FL/Fl_Box.H>
+# include <FL/Fl_Button.H>
+# include <FL/fl_draw.H> /* Same as above. */
+# include <FL/Fl_Double_Window.H>
+# include <FL/Fl_Input.H>
+# include <FL/Fl_Menu_Button.H>
+# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
+# include <FL/Fl_PNG_Image.H>
+# include <FL/Fl_Shared_Image.H>
+# endif
+# include <FL/Fl_Secret_Input.H>
+#else
+# include <cairo-xlib.h>
+# include <gtk/gtk.h>
+# include <gdk/gdkx.h>
+#endif
+
+#include <package-generated.h>
+#include "product-generated.h"
+
+#include <iprt/assert.h>
+#include <iprt/buildconfig.h>
+#include <iprt/env.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+#include <iprt/system.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include <VBox/log.h>
+#include <VBox/VBoxGuestLib.h>
+
+/* The greeter's full name for logging. */
+#define VBOX_MODULE_NAME "vbox-lightdm-greeter"
+
+/* UI elements used in this greeter. */
+#define VBOX_GREETER_UI_WND_GREETER "wnd_greeter"
+
+#define VBOX_GREETER_UI_EDT_USER "edt_username"
+#define VBOX_GREETER_UI_EDT_PASSWORD "edt_password"
+#define VBOX_GREETER_UI_BTN_LOGIN "btn_login"
+#define VBOX_GREETER_UI_LBL_INFO "lbl_info"
+
+/* UI display options. */
+/** Show the restart menu entry / button. */
+#define VBOX_GREETER_UI_SHOW_RESTART RT_BIT(0)
+/** Show the shutdown menu entry / button. */
+#define VBOX_GREETER_UI_SHOW_SHUTDOWN RT_BIT(1)
+/** Show the (customized) top banner. */
+#define VBOX_GREETER_UI_SHOW_BANNER RT_BIT(2)
+/** Enable custom colors */
+#define VBOX_GREETER_UI_USE_THEMING RT_BIT(3)
+
+/** Extracts the 8-bit red component from an uint32_t. */
+#define VBOX_RGB_COLOR_RED(uColor) uColor & 0xFF
+/** Extracts the 8-bit green component from an uint32_t. */
+#define VBOX_RGB_COLOR_GREEN(uColor) (uColor >> 8) & 0xFF
+/** Extracts the 8-bit blue component from an uint32_t. */
+#define VBOX_RGB_COLOR_BLUE(uColor) (uColor >> 16) & 0xFF
+
+#include <VBox/log.h>
+#ifdef VBOX_WITH_GUEST_PROPS
+# include <VBox/HostServices/GuestPropertySvc.h>
+#endif
+
+/** The program name (derived from argv[0]). */
+char *g_pszProgName = (char *)"";
+/** For debugging. */
+#ifdef DEBUG
+ static int g_iVerbosity = 99;
+#else
+ static int g_iVerbosity = 0;
+#endif
+static bool g_fRunning = true;
+
+/** Logging parameters. */
+/** @todo Make this configurable later. */
+static PRTLOGGER g_pLoggerRelease = NULL;
+static uint32_t g_cHistory = 10; /* Enable log rotation, 10 files. */
+static uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /* Max 1 day per file. */
+static uint64_t g_uHistoryFileSize = 100 * _1M; /* Max 100MB per file. */
+
+/**
+ * Context structure which contains all needed
+ * data within callbacks.
+ */
+typedef struct VBOXGREETERCTX
+{
+ /** Pointer to this greeter instance. */
+ LightDMGreeter *pGreeter;
+#ifdef VBOX_WITH_FLTK
+ Fl_Button *pBtnLogin;
+ Fl_Input *pEdtUsername;
+ Fl_Secret_Input *pEdtPassword;
+ Fl_Box *pLblInfo;
+#else
+ /** The GTK builder instance for accessing
+ * the UI elements. */
+ GtkBuilder *pBuilder;
+#endif
+ /** The timeout (in ms) to wait for credentials. */
+ uint32_t uTimeoutMS;
+ /** The starting timestamp (in ms) to calculate
+ * the timeout. */
+ uint64_t uStartMS;
+ /** Timestamp of last abort message. */
+ uint64_t uTsAbort;
+ /** The HGCM client ID. */
+ uint32_t uClientId;
+ /** The credential password. */
+ char *pszPassword;
+} VBOXGREETERCTX, *PVBOXGREETERCTX;
+
+static void vboxGreeterError(const char *pszFormat, ...)
+{
+ va_list va;
+ char *buf;
+ va_start(va, pszFormat);
+ if (RTStrAPrintfV(&buf, pszFormat, va))
+ {
+ RTLogRelPrintf("%s: error: %s", VBOX_MODULE_NAME, buf);
+ RTStrFree(buf);
+ }
+ va_end(va);
+}
+
+static void vboxGreeterLog(const char *pszFormat, ...)
+{
+ if (g_iVerbosity)
+ {
+ va_list va;
+ char *buf;
+ va_start(va, pszFormat);
+ if (RTStrAPrintfV(&buf, pszFormat, va))
+ {
+ /* Only do normal logging in debug mode; could contain
+ * sensitive data! */
+ RTLogRelPrintf("%s: %s", VBOX_MODULE_NAME, buf);
+ RTStrFree(buf);
+ }
+ va_end(va);
+ }
+}
+
+/** @tood Move the following two functions to VbglR3 (also see pam_vbox). */
+#ifdef VBOX_WITH_GUEST_PROPS
+
+/**
+ * Reads a guest property.
+ *
+ * @return IPRT status code.
+ * @param hPAM PAM handle.
+ * @param uClientID Guest property service client ID.
+ * @param pszKey Key (name) of guest property to read.
+ * @param fReadOnly Indicates whether this key needs to be
+ * checked if it only can be read (and *not* written)
+ * by the guest.
+ * @param pszValue Buffer where to store the key's value.
+ * @param cbValue Size of buffer (in bytes).
+ * @param puTimestamp Timestamp of the value
+ * retrieved. Optional.
+ */
+static int vbox_read_prop(uint32_t uClientID,
+ const char *pszKey, bool fReadOnly,
+ char *pszValue, size_t cbValue, uint64_t *puTimestamp)
+{
+ AssertReturn(uClientID, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
+ /* puTimestamp is optional. */
+
+ int rc;
+
+ uint64_t u64Timestamp = 0;
+ char *pszValTemp = NULL;
+ char *pszFlags = NULL;
+ /* The buffer for storing the data and its initial size. We leave a bit
+ * of space here in case the maximum values are raised. */
+ void *pvBuf = NULL;
+ uint32_t cbBuf = GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN + _1K;
+
+ /* Because there is a race condition between our reading the size of a
+ * property and the guest updating it, we loop a few times here and
+ * hope. Actually this should never go wrong, as we are generous
+ * enough with buffer space. */
+ for (unsigned i = 0; i < 10; i++)
+ {
+ pvBuf = RTMemRealloc(pvBuf, cbBuf);
+ if (pvBuf)
+ {
+ rc = VbglR3GuestPropRead(uClientID, pszKey, pvBuf, cbBuf,
+ &pszValTemp, &u64Timestamp, &pszFlags,
+ &cbBuf);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ switch (rc)
+ {
+ case VERR_BUFFER_OVERFLOW:
+ {
+ /* Buffer too small, try it with a bigger one next time. */
+ cbBuf += _1K;
+ continue; /* Try next round. */
+ }
+
+ default:
+ break;
+ }
+
+ /* Everything except VERR_BUFFER_OVERLOW makes us bail out ... */
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Check security bits. */
+ if (pszFlags)
+ {
+ if ( fReadOnly
+ && !RTStrStr(pszFlags, "RDONLYGUEST"))
+ {
+ /* If we want a property which is read-only on the guest
+ * and it is *not* marked as such, deny access! */
+ rc = VERR_ACCESS_DENIED;
+ }
+ }
+ else /* No flags, no access! */
+ rc = VERR_ACCESS_DENIED;
+
+ if (RT_SUCCESS(rc))
+ {
+ /* If everything went well copy property value to our destination buffer. */
+ if (!RTStrPrintf(pszValue, cbValue, "%s", pszValTemp))
+ rc = VERR_BUFFER_OVERFLOW;
+
+ if (puTimestamp)
+ *puTimestamp = u64Timestamp;
+ }
+ }
+
+#ifdef DEBUG
+ vboxGreeterLog("Read guest property \"%s\"=\"%s\" (Flags: %s, TS: %RU64): %Rrc\n",
+ pszKey, pszValTemp ? pszValTemp : "<None>",
+ pszFlags ? pszFlags : "<None>", u64Timestamp, rc);
+#endif
+
+ if (pvBuf)
+ RTMemFree(pvBuf);
+
+ return rc;
+}
+
+# if 0 /* unused */
+/**
+ * Waits for a guest property to be changed.
+ *
+ * @return IPRT status code.
+ * @param hPAM PAM handle.
+ * @param uClientID Guest property service client ID.
+ * @param pszKey Key (name) of guest property to wait for.
+ * @param uTimeoutMS Timeout (in ms) to wait for the change. Specify
+ * RT_INDEFINITE_WAIT to wait indefinitly.
+ */
+static int vbox_wait_prop(uint32_t uClientID,
+ const char *pszKey, uint32_t uTimeoutMS)
+{
+ AssertReturn(uClientID, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
+
+ int rc;
+
+ /* The buffer for storing the data and its initial size. We leave a bit
+ * of space here in case the maximum values are raised. */
+ void *pvBuf = NULL;
+ uint32_t cbBuf = MAX_NAME_LEN + MAX_VALUE_LEN + MAX_FLAGS_LEN + _1K;
+
+ for (int i = 0; i < 10; i++)
+ {
+ void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
+ if (pvTmpBuf)
+ {
+ char *pszName = NULL;
+ char *pszValue = NULL;
+ uint64_t u64TimestampOut = 0;
+ char *pszFlags = NULL;
+
+ pvBuf = pvTmpBuf;
+ rc = VbglR3GuestPropWait(uClientID, pszKey, pvBuf, cbBuf,
+ 0 /* Last timestamp; just wait for next event */, uTimeoutMS,
+ &pszName, &pszValue, &u64TimestampOut,
+ &pszFlags, &cbBuf);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ /* Buffer too small, try it with a bigger one next time. */
+ cbBuf += _1K;
+ continue; /* Try next round. */
+ }
+
+ /* Everything except VERR_BUFFER_OVERLOW makes us bail out ... */
+ break;
+ }
+
+ return rc;
+}
+# endif /* unused */
+
+#endif /* VBOX_WITH_GUEST_PROPS */
+
+/**
+ * Checks for credentials provided by the host / HGCM.
+ *
+ * @return IPRT status code. VERR_NOT_FOUND if no credentials are available,
+ * VINF_SUCCESS on successful retrieval or another IPRT error.
+ * @param pCtx Greeter context.
+ */
+static int vboxGreeterCheckCreds(PVBOXGREETERCTX pCtx)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ static bool s_fCredsNotFoundMsgShown = false;
+ int rc = VbglR3CredentialsQueryAvailability();
+ if (RT_FAILURE(rc))
+ {
+ if (rc != VERR_NOT_FOUND)
+ vboxGreeterError("vboxGreeterCheckCreds: could not query for credentials! rc=%Rrc. Aborting\n", rc);
+ else if (!s_fCredsNotFoundMsgShown)
+ {
+ vboxGreeterLog("vboxGreeterCheckCreds: no credentials available\n");
+ s_fCredsNotFoundMsgShown = true;
+ }
+ }
+ else
+ {
+ /** @todo Domain handling needed? */
+ char *pszUsername; /* User name only is kept local. */
+ char *pszDomain = NULL;
+ rc = VbglR3CredentialsRetrieve(&pszUsername, &pCtx->pszPassword, &pszDomain);
+ if (RT_FAILURE(rc))
+ {
+ vboxGreeterError("vboxGreeterCheckCreds: could not retrieve credentials! rc=%Rrc. Aborting\n", rc);
+ }
+ else
+ {
+ vboxGreeterLog("vboxGreeterCheckCreds: credentials retrieved: user=%s, password=%s, domain=%s\n",
+ pszUsername,
+#ifdef DEBUG
+ pCtx->pszPassword,
+#else
+ "XXX",
+#endif
+ pszDomain);
+ /* Trigger LightDM authentication with the user name just retrieved. */
+ lightdm_greeter_authenticate(pCtx->pGreeter, pszUsername); /* Must be the real user name from host! */
+
+ /* Securely wipe the user name + domain again. */
+ VbglR3CredentialsDestroy(pszUsername, NULL /* pszPassword */, pszDomain,
+ 3 /* Three wipe passes */);
+ }
+ }
+
+#ifdef DEBUG
+ vboxGreeterLog("vboxGreeterCheckCreds: returned with rc=%Rrc\n", rc);
+#endif
+ return rc;
+}
+
+/**
+ * Called by LightDM when greeter is not needed anymore.
+ *
+ * @param signum Signal number.
+ */
+static void cb_sigterm(int signum)
+{
+ RT_NOREF(signum);
+
+ /* Note: This handler must be reentrant-safe. */
+#ifdef VBOX_WITH_FLTK
+ g_fRunning = false;
+#else
+ exit(RTEXITCODE_SUCCESS);
+#endif
+}
+
+/**
+ * Callback for showing a user prompt, issued by the LightDM server.
+ *
+ * @param pGreeter Pointer to this greeter instance.
+ * @param pszText Text to display.
+ * @param enmType Type of prompt to display.
+ * @param pvData Pointer to user-supplied data.
+ */
+static void cb_lightdm_show_prompt(LightDMGreeter *pGreeter,
+ const gchar *pszText, LightDMPromptType enmType,
+ gpointer pvData)
+{
+ vboxGreeterLog("cb_lightdm_show_prompt: text=%s, type=%d\n", pszText, enmType);
+
+ PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
+ AssertPtr(pCtx);
+
+ switch (enmType)
+ {
+ case 1: /* Password. */
+ {
+ if (pCtx->pszPassword)
+ {
+ lightdm_greeter_respond(pGreeter, pCtx->pszPassword);
+ }
+ else
+ {
+#ifdef VBOX_WITH_FLTK
+ AssertPtr(pCtx->pEdtPassword);
+ const char *pszPwd = pCtx->pEdtPassword->value();
+#else
+ GtkEntry *pEdtPwd = GTK_ENTRY(gtk_builder_get_object(pCtx->pBuilder, "edt_password"));
+ AssertPtr(pEdtPwd);
+ const gchar *pszPwd = gtk_entry_get_text(pEdtPwd);
+#endif
+ lightdm_greeter_respond(pGreeter, pszPwd);
+ }
+ break;
+ }
+ /** @todo Other fields? */
+
+ default:
+ break;
+ }
+
+ VbglR3CredentialsDestroy(NULL /* pszUsername */, pCtx->pszPassword, NULL /* pszDomain */,
+ 3 /* Three wipe passes */);
+ pCtx->pszPassword = NULL;
+}
+
+/**
+ * Callback for showing a message, issued by the LightDM server.
+ *
+ * @param pGreeter Pointer to this greeter instance.
+ * @param pszText Text to display.
+ * @param enmType Type of message to display.
+ * @param pvData Pointer to user-supplied data.
+ */
+static void cb_lightdm_show_message(LightDMGreeter *pGreeter,
+ const gchar *pszText, LightDMPromptType enmType,
+ gpointer pvData)
+{
+ RT_NOREF(pGreeter);
+ vboxGreeterLog("cb_lightdm_show_message: text=%s, type=%d\n", pszText, enmType);
+
+ PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
+ AssertPtrReturnVoid(pCtx);
+
+#ifdef VBOX_WITH_FLTK
+ AssertPtr(pCtx->pLblInfo);
+ pCtx->pLblInfo->copy_label(pszText);
+#else
+ GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pCtx->pBuilder, "lbl_info"));
+ AssertPtr(pLblInfo);
+ gtk_label_set_text(pLblInfo, pszText);
+#endif
+}
+
+/**
+ * Callback for authentication completion, issued by the LightDM server.
+ *
+ * @param pGreeter Pointer to this greeter instance.
+ */
+static void cb_lightdm_auth_complete(LightDMGreeter *pGreeter)
+{
+ vboxGreeterLog("cb_lightdm_auth_complete\n");
+
+ const gchar *pszUser = lightdm_greeter_get_authentication_user(pGreeter);
+ vboxGreeterLog("authenticating user: %s\n", pszUser ? pszUser : "<NULL>");
+
+ if (lightdm_greeter_get_is_authenticated(pGreeter))
+ {
+ /** @todo Add non-default session support. */
+ gchar *pszSession = g_strdup(lightdm_greeter_get_default_session_hint(pGreeter));
+ if (pszSession)
+ {
+ vboxGreeterLog("starting session: %s\n", pszSession);
+ GError *pError = NULL;
+ if (!lightdm_greeter_start_session_sync(pGreeter, pszSession, &pError))
+ {
+ vboxGreeterError("unable to start session '%s': %s\n",
+ pszSession, pError ? pError->message : "Unknown error");
+ }
+ else
+ {
+ AssertPtr(pszSession);
+ vboxGreeterLog("session '%s' successfully started\n", pszSession);
+ }
+ if (pError)
+ g_error_free(pError);
+ g_free(pszSession);
+ }
+ else
+ vboxGreeterError("unable to get default session\n");
+ }
+ else
+ vboxGreeterLog("user not authenticated successfully (yet)\n");
+}
+
+/**
+ * Callback for clicking on the "Login" button.
+ *
+ * @param pWidget Widget this callback is bound to.
+ * @param pvData Pointer to user-supplied data.
+ */
+#ifdef VBOX_WITH_FLTK
+void cb_btn_login(Fl_Widget *pWidget, void *pvData)
+#else
+void cb_btn_login(GtkWidget *pWidget, gpointer pvData)
+#endif
+{
+ PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
+ RT_NOREF(pWidget);
+ AssertPtr(pCtx);
+
+#ifdef VBOX_WITH_FLTK
+ AssertPtr(pCtx->pEdtUsername);
+ const char *pszUser = pCtx->pEdtUsername->value();
+ AssertPtr(pCtx->pEdtPassword);
+ const char *pszPwd = pCtx->pEdtPassword->value();
+#else
+ GtkEntry *pEdtUser = GTK_ENTRY(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_EDT_USER));
+ AssertPtr(pEdtUser);
+ const gchar *pszUser = gtk_entry_get_text(pEdtUser);
+
+ GtkEntry *pEdtPwd = GTK_ENTRY(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_EDT_PASSWORD));
+ AssertPtr(pEdtPwd);
+ const gchar *pszPwd = gtk_entry_get_text(pEdtPwd);
+#endif
+
+ /** @todo Add domain handling? */
+ vboxGreeterLog("login button pressed: greeter=%p, user=%s, password=%s\n",
+ pCtx->pGreeter,
+ pszUser ? pszUser : "<NONE>",
+#ifdef DEBUG
+ pszPwd ? pszPwd : "<NONE>");
+#else
+ /* Don't log passwords in release mode! */
+ "XXX");
+#endif
+ if (strlen(pszUser)) /* Only authenticate if username is given. */
+ {
+ lightdm_greeter_respond(pCtx->pGreeter, pszPwd);
+ lightdm_greeter_authenticate(pCtx->pGreeter, pszUser);
+ }
+}
+
+/**
+ * Callback for clicking on the "Menu" button.
+ *
+ * @param pWidget Widget this callback is bound to.
+ * @param pvData Pointer to user-supplied data.
+ */
+#ifdef VBOX_WITH_FLTK
+void cb_btn_menu(Fl_Widget *pWidget, void *pvData)
+#else
+void cb_btn_menu(GtkWidget *pWidget, gpointer pvData)
+#endif
+{
+ RT_NOREF(pWidget, pvData);
+ vboxGreeterLog("menu button pressed\n");
+}
+
+/**
+ * Callback for clicking on the "Restart" button / menu entry.
+ *
+ * @param pWidget Widget this callback is bound to.
+ * @param pvData Pointer to user-supplied data.
+ */
+#ifdef VBOX_WITH_FLTK
+void cb_btn_restart(Fl_Widget *pWidget, void *pvData)
+#else
+void cb_btn_restart(GtkWidget *pWidget, gpointer pvData)
+#endif
+{
+ RT_NOREF(pWidget, pvData);
+ vboxGreeterLog("restart button pressed\n");
+
+ bool fRestart = true;
+#ifdef VBOX_WITH_FLTK
+ int rc = fl_choice("Really restart the system?", "Yes", "No", NULL);
+ fRestart = rc == 0;
+#endif
+
+ if (fRestart)
+ {
+ vboxGreeterLog("restart requested\n");
+#ifndef DEBUG
+ lightdm_restart(NULL);
+#endif
+ }
+}
+
+/**
+ * Callback for clicking on the "Shutdown" button / menu entry.
+ *
+ * @param pWidget Widget this callback is bound to.
+ * @param pvData Pointer to user-supplied data.
+ */
+#ifdef VBOX_WITH_FLTK
+void cb_btn_shutdown(Fl_Widget *pWidget, void *pvData)
+#else
+void cb_btn_shutdown(GtkWidget *pWidget, gpointer pvData)
+#endif
+{
+ RT_NOREF(pWidget, pvData);
+ vboxGreeterLog("shutdown button pressed\n");
+
+ bool fShutdown = true;
+#ifdef VBOX_WITH_FLTK
+ int rc = fl_choice("Really shutdown the system?", "Yes", "No", NULL);
+ fShutdown = rc == 0;
+#endif
+
+ if (fShutdown)
+ {
+ vboxGreeterLog("shutdown requested\n");
+#ifndef DEBUG
+ lightdm_shutdown(NULL);
+#endif
+ }
+}
+
+#ifdef VBOX_WITH_FLTK
+void cb_edt_username(Fl_Widget *pWidget, void *pvData)
+#else
+void cb_edt_username(GtkWidget *pWidget, gpointer pvData)
+#endif
+{
+ RT_NOREF(pWidget);
+ vboxGreeterLog("cb_edt_username called\n");
+
+ PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
+ AssertPtr(pCtx);
+#ifdef VBOX_WITH_FLTK
+ AssertPtr(pCtx->pEdtPassword);
+ Fl::focus(pCtx->pEdtPassword);
+#endif
+}
+
+#ifdef VBOX_WITH_FLTK
+void cb_edt_password(Fl_Widget *pWidget, void *pvData)
+#else
+void cb_edt_password(GtkWidget *pWidget, gpointer pvData)
+#endif
+{
+ RT_NOREF(pWidget, pvData);
+ vboxGreeterLog("cb_edt_password called\n");
+
+ PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
+ AssertPtr(pCtx);
+#ifdef VBOX_WITH_FLTK
+ AssertPtr(pCtx->pBtnLogin);
+ cb_btn_login(pCtx->pBtnLogin, pvData);
+#endif
+}
+
+/**
+ * Callback for the timer event which is checking for new credentials
+ * from the host.
+ *
+ * @param pvData Pointer to user-supplied data.
+ */
+#ifdef VBOX_WITH_FLTK
+static void cb_check_creds(void *pvData)
+#else
+static gboolean cb_check_creds(gpointer pvData)
+#endif
+{
+ PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
+ AssertPtr(pCtx);
+
+#ifdef DEBUG
+ vboxGreeterLog("cb_check_creds called, clientId=%RU32, timeoutMS=%RU32\n",
+ pCtx->uClientId, pCtx->uTimeoutMS);
+#endif
+
+ int rc = VINF_SUCCESS;
+
+#ifdef VBOX_WITH_GUEST_PROPS
+ bool fAbort = false;
+ char szVal[255];
+ if (pCtx->uClientId)
+ {
+ uint64_t tsAbort;
+ rc = vbox_read_prop(pCtx->uClientId,
+ "/VirtualBox/GuestAdd/PAM/CredsWaitAbort",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), &tsAbort);
+ switch (rc)
+ {
+ case VINF_SUCCESS:
+# ifdef DEBUG
+ vboxGreeterLog("cb_check_creds: tsAbort %RU64 <-> %RU64\n",
+ pCtx->uTsAbort, tsAbort);
+# endif
+ if (tsAbort != pCtx->uTsAbort)
+ fAbort = true; /* Timestamps differs, abort. */
+ pCtx->uTsAbort = tsAbort;
+ break;
+
+ case VERR_TOO_MUCH_DATA:
+ vboxGreeterError("cb_check_creds: temporarily unable to get abort notification\n");
+ break;
+
+ case VERR_NOT_FOUND:
+ /* Value not found, continue checking for credentials. */
+ break;
+
+ default:
+ vboxGreeterError("cb_check_creds: the abort notification request failed with rc=%Rrc\n", rc);
+ fAbort = true; /* Abort on error. */
+ break;
+ }
+ }
+
+ if (fAbort)
+ {
+ /* Get optional message. */
+ szVal[0] = '\0';
+ int rc2 = vbox_read_prop(pCtx->uClientId,
+ "/VirtualBox/GuestAdd/PAM/CredsMsgWaitAbort",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), NULL /* Timestamp. */);
+ if ( RT_FAILURE(rc2)
+ && rc2 != VERR_NOT_FOUND)
+ vboxGreeterError("cb_check_creds: getting wait abort message failed with rc=%Rrc\n", rc2);
+# ifdef VBOX_WITH_FLTK
+ AssertPtr(pCtx->pLblInfo);
+ pCtx->pLblInfo->copy_label(szVal);
+# else /* !VBOX_WITH_FLTK */
+ GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_LBL_INFO));
+ AssertPtr(pLblInfo);
+ gtk_label_set_text(pLblInfo, szVal);
+# endif /* !VBOX_WITH_FLTK */
+ vboxGreeterLog("cb_check_creds: got notification from host to abort waiting\n");
+ }
+ else
+ {
+#endif /* VBOX_WITH_GUEST_PROPS */
+ rc = vboxGreeterCheckCreds(pCtx);
+ if (RT_SUCCESS(rc))
+ {
+ /* Credentials retrieved. */
+ }
+ else if (rc == VERR_NOT_FOUND)
+ {
+ /* No credentials found, but try next round (if there's
+ * time left for) ... */
+ }
+#ifdef VBOX_WITH_GUEST_PROPS
+ }
+#endif /* VBOX_WITH_GUEST_PROPS */
+
+ if (rc == VERR_NOT_FOUND) /* No credential found this round. */
+ {
+ /* Calculate timeout value left after process has been started. */
+ uint64_t u64Elapsed = RTTimeMilliTS() - pCtx->uStartMS;
+ /* Is it time to bail out? */
+ if (pCtx->uTimeoutMS < u64Elapsed)
+ {
+#ifdef VBOX_WITH_GUEST_PROPS
+ szVal[0] = '\0';
+ int rc2 = vbox_read_prop(pCtx->uClientId,
+ "/VirtualBox/GuestAdd/PAM/CredsMsgWaitTimeout",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), NULL /* Timestamp. */);
+ if ( RT_FAILURE(rc2)
+ && rc2 != VERR_NOT_FOUND)
+ vboxGreeterError("cb_check_creds: getting wait timeout message failed with rc=%Rrc\n", rc2);
+# ifdef VBOX_WITH_FLTK
+ AssertPtr(pCtx->pLblInfo);
+ pCtx->pLblInfo->copy_label(szVal);
+# else
+ GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_LBL_INFO));
+ AssertPtr(pLblInfo);
+ gtk_label_set_text(pLblInfo, szVal);
+# endif
+#endif /* VBOX_WITH_GUEST_PROPS */
+ vboxGreeterLog("cb_check_creds: no credentials retrieved within time (%RU32ms), giving up\n",
+ pCtx->uTimeoutMS);
+ rc = VERR_TIMEOUT;
+ }
+ }
+
+#ifdef DEBUG
+ vboxGreeterLog("cb_check_creds returned with rc=%Rrc\n", rc);
+#endif
+
+ /* At the moment we only allow *one* shot from the host,
+ * so setting credentials in a second attempt won't be possible
+ * intentionally. */
+
+ if (rc == VERR_NOT_FOUND)
+#ifdef VBOX_WITH_FLTK
+ Fl::repeat_timeout(0.5 /* 500 ms */, cb_check_creds, pvData);
+#else
+ return TRUE; /* No credentials found, do another round. */
+
+ return FALSE; /* Remove timer source on every other error / status. */
+#endif
+}
+
+/**
+ * Release logger callback.
+ *
+ * @return IPRT status code.
+ * @param pLoggerRelease
+ * @param enmPhase
+ * @param pfnLog
+ */
+static DECLCALLBACK(void) vboxGreeterLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
+{
+ /* Some introductory information. */
+ static RTTIMESPEC s_TimeSpec;
+ char szTmp[256];
+ if (enmPhase == RTLOGPHASE_BEGIN)
+ RTTimeNow(&s_TimeSpec);
+ RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
+
+ switch (enmPhase)
+ {
+ case RTLOGPHASE_BEGIN:
+ {
+ pfnLog(pLoggerRelease,
+ "vbox-greeter %s r%s (verbosity: %d) %s (%s %s) release log\n"
+ "Log opened %s\n",
+ RTBldCfgVersion(), RTBldCfgRevisionStr(), g_iVerbosity, VBOX_BUILD_TARGET,
+ __DATE__, __TIME__, szTmp);
+
+ int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
+
+ /* the package type is interesting for Linux distributions */
+ char szExecName[RTPATH_MAX];
+ char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
+ pfnLog(pLoggerRelease,
+ "Executable: %s\n"
+ "Process ID: %u\n"
+ "Package type: %s"
+#ifdef VBOX_OSE
+ " (OSE)"
+#endif
+ "\n",
+ pszExecName ? pszExecName : "unknown",
+ RTProcSelf(),
+ VBOX_PACKAGE_STRING);
+ break;
+ }
+
+ case RTLOGPHASE_PREROTATE:
+ pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
+ break;
+
+ case RTLOGPHASE_POSTROTATE:
+ pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
+ break;
+
+ case RTLOGPHASE_END:
+ pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
+ break;
+
+ default:
+ /* nothing */;
+ }
+}
+
+/**
+ * Creates the default release logger outputting to the specified file.
+ *
+ * @return IPRT status code.
+ * @param pszLogFile Filename for log output. Optional.
+ */
+static int vboxGreeterLogCreate(const char *pszLogFile)
+{
+ /* Create release logger (stdout + file). */
+ static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
+ RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ fFlags |= RTLOGFLAGS_USECRLF;
+#endif
+ int rc = RTLogCreateEx(&g_pLoggerRelease, fFlags, "all",
+ "VBOXGREETER_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
+ RTLOGDEST_STDOUT,
+ vboxGreeterLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
+ NULL /*pErrInfo*/, pszLogFile);
+ if (RT_SUCCESS(rc))
+ {
+ /* register this logger as the release logger */
+ RTLogRelSetDefaultInstance(g_pLoggerRelease);
+
+ /* Explicitly flush the log in case of VBOXGREETER_RELEASE_LOG_FLAGS=buffered. */
+ RTLogFlush(g_pLoggerRelease);
+ }
+
+ return rc;
+}
+
+static void vboxGreeterLogDestroy(void)
+{
+ RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
+}
+
+static int vboxGreeterUsage(void)
+{
+ RTPrintf("Usage:\n"
+ " %-12s [-h|-?|--help] [-F|--logfile <file>]\n"
+ " [-v|--verbose] [-V|--version]\n", g_pszProgName);
+
+ RTPrintf("\n"
+ " Copyright (C) 2012-" VBOX_C_YEAR " " VBOX_VENDOR "\n");
+
+ return RTEXITCODE_SYNTAX;
+}
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+ g_pszProgName = RTPathFilename(argv[0]);
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--logfile", 'F', RTGETOPT_REQ_STRING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--version", 'V', RTGETOPT_REQ_NOTHING }
+ };
+
+ char szLogFile[RTPATH_MAX + 128] = "";
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv,
+ s_aOptions, RT_ELEMENTS(s_aOptions),
+ 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion))
+ && RT_SUCCESS(rc))
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ case 'F':
+ if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", ValueUnion.psz))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get prepare log file name");
+ break;
+
+ case 'h':
+ case '?':
+ return vboxGreeterUsage();
+
+ case 'v': /* Raise verbosity. */
+ g_iVerbosity++;
+ break;
+
+ case 'V': /* Print version and exit. */
+ RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
+ return RTEXITCODE_SUCCESS;
+ break; /* Never reached. */
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ return RTEXITCODE_SYNTAX;
+
+ rc = VbglR3InitUser();
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to init Vbgl (%Rrc)", rc);
+
+ rc = vboxGreeterLogCreate(strlen(szLogFile) ? szLogFile : NULL);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log (%s, %Rrc)",
+ strlen(szLogFile) ? szLogFile : "<None>", rc);
+
+ vboxGreeterLog("init\n");
+
+ signal(SIGTERM, cb_sigterm);
+
+ /** @todo This function already is too long. Move code into
+ * functions. */
+
+ VBOXGREETERCTX ctx;
+ RT_ZERO(ctx);
+
+ /* UI parameters. */
+ uint32_t uBgColor = 0; /* The background color. */
+ uint32_t uLogonDlgHdrColor = 0;
+ uint32_t uLogonDlgBgColor = 0; /* The greeter's dialog color. */
+ uint32_t uLogonDlgBtnColor = 0; /* The greeter's button color. */
+
+#ifdef VBOX_GREETER_WITH_PNG_SUPPORT
+ char szBannerPath[RTPATH_MAX];
+#endif
+
+ /* By default most UI elements are shown. */
+ uint32_t uOptsUI = VBOX_GREETER_UI_SHOW_RESTART
+ | VBOX_GREETER_UI_SHOW_SHUTDOWN;
+#ifdef VBOX_WITH_GUEST_PROPS
+ uint32_t uClientId = 0;
+ rc = VbglR3GuestPropConnect(&uClientId);
+ if (RT_SUCCESS(rc))
+ {
+ vboxGreeterLog("clientId=%RU32\n", uClientId);
+
+ ctx.uClientId = uClientId;
+
+ char szVal[256];
+ int rc2 = vbox_read_prop(uClientId,
+ "/VirtualBox/GuestAdd/Greeter/HideRestart",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), NULL /* Timestamp. */);
+ if ( RT_SUCCESS(rc2)
+ && !RTStrICmp(szVal, "1"))
+ {
+ uOptsUI &= ~VBOX_GREETER_UI_SHOW_RESTART;
+ }
+
+ rc2 = vbox_read_prop(uClientId,
+ "/VirtualBox/GuestAdd/Greeter/HideShutdown",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), NULL /* Timestamp. */);
+ if ( RT_SUCCESS(rc2)
+ && !RTStrICmp(szVal, "1"))
+ {
+ uOptsUI &= ~VBOX_GREETER_UI_SHOW_SHUTDOWN;
+ }
+
+# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
+ /* Load the banner. */
+ rc2 = vbox_read_prop(uClientId,
+ "/VirtualBox/GuestAdd/Greeter/BannerPath",
+ true /* Read-only on guest */,
+ szBannerPath, sizeof(szBannerPath), NULL /* Timestamp. */);
+ if (RT_SUCCESS(rc2))
+ {
+ if (RTFileExists(szBannerPath))
+ {
+ vboxGreeterLog("showing banner from '%s'\n", szBannerPath);
+ uOptsUI |= VBOX_GREETER_UI_SHOW_BANNER;
+ }
+ else
+ vboxGreeterLog("warning: unable to find banner at '%s', skipping\n", szBannerPath);
+ }
+# endif /* VBOX_GREETER_WITH_PNG_SUPPORT */
+
+ /* Use theming?. */
+ rc2 = vbox_read_prop(uClientId,
+ "/VirtualBox/GuestAdd/Greeter/UseTheming",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), NULL /* Timestamp. */);
+ if ( RT_SUCCESS(rc2)
+ && !RTStrICmp(szVal, "1"))
+ {
+ vboxGreeterLog("custom theming enabled\n");
+ uOptsUI |= VBOX_GREETER_UI_USE_THEMING;
+ }
+
+ if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
+ {
+ /* Get background color. */
+ rc2 = vbox_read_prop(uClientId,
+ "/VirtualBox/GuestAdd/Greeter/Theme/BackgroundColor",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), NULL /* Timestamp. */);
+ if (RT_SUCCESS(rc2))
+ {
+ uBgColor = strtol(szVal, NULL,
+ /* Change conversion base when having a 0x prefix. */
+ RTStrStr(szVal, "0x") == szVal ? 0 : 16);
+ }
+
+ /* Logon dialog. */
+
+ /* Get header color. */
+ rc2 = vbox_read_prop(uClientId,
+ "/VirtualBox/GuestAdd/Greeter/Theme/LogonDialog/HeaderColor",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), NULL /* Timestamp. */);
+ if (RT_SUCCESS(rc2))
+ {
+ uLogonDlgHdrColor = strtol(szVal, NULL,
+ /* Change conversion base when having a 0x prefix. */
+ RTStrStr(szVal, "0x") == szVal ? 0 : 16);
+ }
+
+ /* Get dialog color. */
+ rc2 = vbox_read_prop(uClientId,
+ "/VirtualBox/GuestAdd/Greeter/Theme/LogonDialog/BackgroundColor",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), NULL /* Timestamp. */);
+ if (RT_SUCCESS(rc2))
+ {
+ uLogonDlgBgColor = strtol(szVal, NULL,
+ /* Change conversion base when having a 0x prefix. */
+ RTStrStr(szVal, "0x") == szVal ? 0 : 16);
+ }
+
+ /* Get button color. */
+ rc2 = vbox_read_prop(uClientId,
+ "/VirtualBox/GuestAdd/Greeter/Theme/LogonDialog/ButtonColor",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), NULL /* Timestamp. */);
+ if (RT_SUCCESS(rc2))
+ {
+ uLogonDlgBtnColor = strtol(szVal, NULL,
+ /* Change conversion base when having a 0x prefix. */
+ RTStrStr(szVal, "0x") == szVal ? 0 : 16);
+ }
+ }
+ }
+ else
+ vboxGreeterError("unable to connect to guest property service, rc=%Rrc\n", rc);
+#endif
+ vboxGreeterLog("UI options are: %RU32\n", uOptsUI);
+
+#ifdef VBOX_WITH_FLTK
+ int rc2 = Fl::scheme("plastic");
+ if (!rc2)
+ vboxGreeterLog("warning: unable to set visual scheme\n");
+
+ Fl::visual(FL_DOUBLE | FL_INDEX);
+ Fl_Double_Window *pWndMain = new Fl_Double_Window(Fl::w(), Fl::h(), "VirtualBox Guest Additions");
+ AssertPtr(pWndMain);
+ if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
+ pWndMain->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uBgColor),
+ VBOX_RGB_COLOR_GREEN(uBgColor),
+ VBOX_RGB_COLOR_BLUE(uBgColor)));
+ else /* Default colors. */
+ pWndMain->color(fl_rgb_color(0x73, 0x7F, 0x8C));
+
+ Fl_Double_Window *pWndGreeter = new Fl_Double_Window(500, 350);
+ AssertPtr(pWndGreeter);
+ pWndGreeter->set_modal();
+ if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
+ pWndGreeter->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgBgColor),
+ VBOX_RGB_COLOR_GREEN(uLogonDlgBgColor),
+ VBOX_RGB_COLOR_BLUE(uLogonDlgBgColor)));
+ else /* Default colors. */
+ pWndGreeter->color(fl_rgb_color(255, 255, 255));
+
+ uint32_t uOffsetX = 130;
+ /**
+ * For now we're using a simple Y offset for moving all elements
+ * down if a banner needs to be shown on top of the greeter. Not
+ * very clean but does the job. Use some more layouting stuff
+ * when this gets more complex.
+ */
+ uint32_t uOffsetY = 80;
+
+# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
+ fl_register_images();
+
+ /** @todo Add basic image type detection based on file
+ * extension. */
+
+ Fl_PNG_Image *pImgBanner = NULL;
+ if (uOptsUI & VBOX_GREETER_UI_SHOW_BANNER)
+ {
+ pImgBanner = new Fl_PNG_Image(szBannerPath);
+ AssertPtr(pImgBanner);
+
+ /** @todo Make the banner size configurable via guest
+ * properties. For now it's hardcoded to 460 x 90px. */
+ Fl_Box *pBoxBanner = new Fl_Box(20, uOffsetY, 460, 90, "");
+ AssertPtr(pBoxBanner);
+ pBoxBanner->image(pImgBanner);
+
+ uOffsetY = 120;
+ }
+# endif
+
+ Fl_Box *pLblHeader = new Fl_Box(FL_NO_BOX, 242, uOffsetY, 300, 20,
+ "Desktop Login");
+ AssertPtr(pLblHeader);
+
+ /** Note to use an own font:
+ * Fl_Font myfnt = FL_FREE_FONT + 1;
+ * Fl::set_font(myfnt, "MyFont"); */
+ Fl_Font fntHeader = FL_FREE_FONT;
+ Fl::set_font(fntHeader, "Courier");
+
+ pLblHeader->align(FL_ALIGN_LEFT);
+ pLblHeader->labelfont(FL_BOLD);
+ pLblHeader->labelsize(24);
+ if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
+ pLblHeader->labelcolor(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgHdrColor),
+ VBOX_RGB_COLOR_GREEN(uLogonDlgHdrColor),
+ VBOX_RGB_COLOR_BLUE(uLogonDlgHdrColor)));
+ else /* Default color. */
+ pLblHeader->labelcolor(fl_rgb_color(0x51, 0x5F, 0x77));
+ uOffsetY += 40;
+
+ /** @todo Add basic NLS support. */
+
+ Fl_Input *pEdtUsername = new Fl_Input(uOffsetX, uOffsetY,
+ 300, 20, "User Name");
+ AssertPtr(pEdtUsername);
+ pEdtUsername->callback(cb_edt_username, &ctx);
+ pEdtUsername->when(FL_WHEN_ENTER_KEY_ALWAYS);
+ Fl::focus(pEdtUsername);
+ ctx.pEdtUsername = pEdtUsername;
+
+ Fl_Secret_Input *pEdtPassword = new Fl_Secret_Input(uOffsetX, uOffsetY + 40,
+ 300, 20, "Password");
+ AssertPtr(pEdtPassword);
+ pEdtPassword->callback(cb_edt_password, &ctx);
+ pEdtPassword->when(FL_WHEN_ENTER_KEY_ALWAYS);
+ ctx.pEdtPassword = pEdtPassword;
+
+ Fl_Button *pBtnLogin = new Fl_Button(uOffsetX, uOffsetY + 70,
+ 100, 40, "Log In");
+ AssertPtr(pBtnLogin);
+ pBtnLogin->callback(cb_btn_login, &ctx);
+ if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
+ pBtnLogin->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgBtnColor),
+ VBOX_RGB_COLOR_GREEN(uLogonDlgBtnColor),
+ VBOX_RGB_COLOR_BLUE(uLogonDlgBtnColor)));
+ else /* Default color. */
+ pBtnLogin->color(fl_rgb_color(255, 255, 255));
+ ctx.pBtnLogin = pBtnLogin;
+
+ Fl_Menu_Button *pBtnMenu = new Fl_Menu_Button(uOffsetX + 120, uOffsetY + 70,
+ 100, 40, "Options");
+ AssertPtr(pBtnMenu);
+ pBtnMenu->callback(cb_btn_menu, &ctx);
+ if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
+ pBtnMenu->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgBtnColor),
+ VBOX_RGB_COLOR_GREEN(uLogonDlgBtnColor),
+ VBOX_RGB_COLOR_BLUE(uLogonDlgBtnColor)));
+ else /* Default color. */
+ pBtnMenu->color(fl_rgb_color(255, 255, 255));
+
+ if (uOptsUI & VBOX_GREETER_UI_SHOW_RESTART)
+ pBtnMenu->add("Restart", "" /* Shortcut */, cb_btn_restart, &ctx, 0 /* Flags */);
+ if (uOptsUI & VBOX_GREETER_UI_SHOW_SHUTDOWN)
+ pBtnMenu->add("Shutdown", "" /* Shortcut */, cb_btn_shutdown, &ctx, 0 /* Flags */);
+
+ char szLabel[255];
+ RTStrPrintf(szLabel, sizeof(szLabel), "Oracle VM VirtualBox Guest Additions %sr%s",
+ RTBldCfgVersion(), RTBldCfgRevisionStr());
+ Fl_Box *pLblInfo = new Fl_Box(FL_NO_BOX , 50, uOffsetY + 150,
+ 400, 20, szLabel);
+ AssertPtr(pLblInfo);
+ ctx.pLblInfo = pLblInfo;
+
+ pWndGreeter->end();
+ pWndGreeter->position((Fl::w() - pWndGreeter->w()) / 2,
+ (Fl::h() - pWndGreeter->h()) / 2);
+
+ pWndMain->fullscreen();
+ pWndMain->show(argc, argv);
+ pWndMain->end();
+
+ pWndGreeter->show();
+#else /* !VBOX_WITH_FLTK */
+ gtk_init(&argc, &argv);
+
+ /* Set default cursor */
+ gdk_window_set_cursor(gdk_get_default_root_window(), gdk_cursor_new(GDK_LEFT_PTR));
+
+ GError *pError = NULL;
+ GtkBuilder *pBuilder = gtk_builder_new();
+ AssertPtr(pBuilder);
+ if (!gtk_builder_add_from_file(pBuilder, "/usr/share/xgreeters/vbox-greeter.ui", &pError))
+ {
+ AssertPtr(pError);
+ vboxGreeterError("unable to load UI: %s", pError->message);
+ return RTEXITCODE_FAILURE;
+ }
+
+ GtkWindow *pWndGreeter = GTK_WINDOW(gtk_builder_get_object(pBuilder, VBOX_GREETER_UI_WND_GREETER));
+ AssertPtr(pWndGreeter);
+ GtkButton *pBtnLogin = GTK_BUTTON(gtk_builder_get_object(pBuilder, VBOX_GREETER_UI_BTN_LOGIN));
+ AssertPtr(pBtnLogin);
+ GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pBuilder, VBOX_GREETER_UI_LBL_INFO));
+ AssertPtr(pLblInfo);
+
+ ctx.pBuilder = pBuilder;
+
+ g_signal_connect(G_OBJECT(pBtnLogin), "clicked", G_CALLBACK(cb_btn_login), &ctx);
+
+ GdkRectangle rectScreen;
+ gdk_screen_get_monitor_geometry(gdk_screen_get_default(), gdk_screen_get_primary_monitor(gdk_screen_get_default()), &rectScreen);
+ vboxGreeterLog("monitor (default) is %dx%d\n", rectScreen.width, rectScreen.height);
+
+ gint iWndX, iWndY;
+ gtk_window_get_default_size(pWndGreeter, &iWndX, &iWndY);
+ vboxGreeterLog("greeter is %dx%d\n", iWndX, iWndY);
+
+ gtk_window_move(pWndGreeter,
+ (rectScreen.width / 2) - (iWndX / 2),
+ (rectScreen.height / 2) - (iWndY / 2));
+ gtk_widget_show(GTK_WIDGET(pWndGreeter));
+
+ g_clear_error(&pError);
+#endif /* !VBOX_WITH_FLTK */
+
+ /* GType is needed in any case (for LightDM), whether we
+ * use GTK3 or not. */
+ g_type_init();
+
+ GMainLoop *pMainLoop = g_main_loop_new(NULL, FALSE /* Not yet running */);
+ AssertPtr(pMainLoop); NOREF(pMainLoop);
+
+ LightDMGreeter *pGreeter = lightdm_greeter_new();
+ AssertPtr(pGreeter);
+
+ g_signal_connect(pGreeter, "show-prompt", G_CALLBACK(cb_lightdm_show_prompt), &ctx);
+ g_signal_connect(pGreeter, "show-message", G_CALLBACK(cb_lightdm_show_message), &ctx);
+ g_signal_connect(pGreeter, "authentication-complete", G_CALLBACK(cb_lightdm_auth_complete), &ctx);
+
+ ctx.pGreeter = pGreeter;
+
+ if (!lightdm_greeter_connect_sync(pGreeter, NULL))
+ {
+ vboxGreeterError("unable to connect to LightDM server, aborting\n");
+ return RTEXITCODE_FAILURE;
+ }
+
+ vboxGreeterLog("connected to LightDM server\n");
+
+#ifdef VBOX_WITH_GUEST_PROPS
+ bool fCheckCreds = false;
+ if (uClientId) /* Connected to guest property service? */
+ {
+ char szVal[256];
+ rc2 = vbox_read_prop(uClientId,
+ "/VirtualBox/GuestAdd/PAM/CredsWait",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), NULL /* Timestamp. */);
+ if (RT_SUCCESS(rc2))
+ {
+ uint32_t uTimeoutMS = RT_INDEFINITE_WAIT; /* Wait infinite by default. */
+ rc2 = vbox_read_prop(uClientId,
+ "/VirtualBox/GuestAdd/PAM/CredsWaitTimeout",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), NULL /* Timestamp. */);
+ if (RT_SUCCESS(rc2))
+ {
+ uTimeoutMS = RTStrToUInt32(szVal);
+ if (!uTimeoutMS)
+ {
+ vboxGreeterError("pam_vbox_authenticate: invalid waiting timeout value specified, defaulting to infinite timeout\n");
+ uTimeoutMS = RT_INDEFINITE_WAIT;
+ }
+ else
+ uTimeoutMS = uTimeoutMS * 1000; /* Make ms out of s. */
+ }
+
+ ctx.uTimeoutMS = uTimeoutMS;
+
+ rc2 = vbox_read_prop(uClientId,
+ "/VirtualBox/GuestAdd/PAM/CredsMsgWaiting",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), NULL /* Timestamp. */);
+ if (RT_SUCCESS(rc2))
+ {
+# ifdef VBOX_WITH_FLTK
+ Assert(pLblInfo);
+ pLblInfo->copy_label(szVal);
+# else
+ gtk_label_set_text(pLblInfo, szVal);
+# endif
+ }
+
+ /* Get initial timestamp so that we can compare the time
+ * whether the value has been changed or not in our event callback. */
+ vbox_read_prop(uClientId,
+ "/VirtualBox/GuestAdd/PAM/CredsWaitAbort",
+ true /* Read-only on guest */,
+ szVal, sizeof(szVal), &ctx.uTsAbort);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Before we actuall wait for credentials just make sure we didn't already get credentials
+ * set so that we can skip waiting for them ... */
+ rc2 = vboxGreeterCheckCreds(&ctx);
+ if (rc2 == VERR_NOT_FOUND)
+ {
+ /* Get current time stamp to later calculate rest of timeout left. */
+ ctx.uStartMS = RTTimeMilliTS();
+
+ fCheckCreds = true;
+ }
+ }
+ }
+
+ /* Start the timer to check credentials availability. */
+ if (fCheckCreds)
+ {
+ vboxGreeterLog("No credentials available on startup, starting to check periodically ...\n");
+# ifdef VBOX_WITH_FLTK
+ Fl::add_timeout(0.5 /* 500 ms */, cb_check_creds, &ctx);
+# else
+ g_timeout_add(500 /* ms */, (GSourceFunc)cb_check_creds, &ctx);
+# endif
+ }
+ }
+#endif /* VBOX_WITH_GUEST_PROPS */
+
+#ifdef VBOX_WITH_FLTK
+ /*
+ * Do own GDK main loop processing because FLTK also needs
+ * to have the chance of processing its events.
+ */
+ GMainContext *pMainCtx = g_main_context_default();
+ AssertPtr(pMainCtx);
+
+ while (g_fRunning)
+ {
+ g_main_context_iteration(pMainCtx,
+ FALSE /* No blocking */);
+ Fl::check();
+ RTThreadSleep(10); /* Wait a bit, don't hog the CPU too much. */
+ }
+
+ g_main_context_unref(pMainCtx);
+
+# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
+ if (pImgBanner)
+ {
+ delete pImgBanner; /* Call destructor to free bitmap data. */
+ pImgBanner = NULL;
+ }
+# endif /* VBOX_GREETER_WITH_PNG_SUPPORT */
+#else /* !VBOX_WITH_FLTK */
+ gtk_main();
+ /** @todo Never reached so far. LightDM sends a SIGTERM. */
+#endif /* !VBOX_WITH_FLTK */
+
+ vboxGreeterLog("terminating\n");
+
+#ifdef VBOX_WITH_GUEST_PROPS
+ if (uClientId)
+ {
+ rc2 = VbglR3GuestPropDisconnect(uClientId);
+ AssertRC(rc2);
+ }
+#endif /* VBOX_WITH_GUEST_PROPS */
+
+ VbglR3Term();
+
+ RTEXITCODE rcExit = RT_SUCCESS(rc)
+ ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+
+ vboxGreeterLog("terminated with exit code %ld (rc=%Rrc)\n",
+ rcExit, rc);
+
+ vboxGreeterLogDestroy();
+
+ return rcExit;
+}
+
+#ifdef DEBUG
+DECLEXPORT(void) RTAssertMsg1Weak(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ RTAssertMsg1(pszExpr, uLine, pszFile, pszFunction);
+}
+#endif
+
diff --git a/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.desktop b/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.desktop
new file mode 100644
index 00000000..b3946b38
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.desktop
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Name=VirtualBox Greeter
+Comment=Provides Single Sign-On (SSO) support
+Exec=/usr/sbin/vbox-greeter
+Type=Application
diff --git a/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.ui b/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.ui
new file mode 100644
index 00000000..76719b39
--- /dev/null
+++ b/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.ui
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="wnd_greeter">
+ <property name="width_request">512</property>
+ <property name="height_request">348</property>
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">VirtualBox Guest Additions Login</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="window_position">center</property>
+ <property name="default_width">512</property>
+ <property name="default_height">348</property>
+ <property name="has_resize_grip">False</property>
+ <child>
+ <object class="GtkNotebook" id="nb_greeter">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkDrawingArea" id="drawingarea1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkAspectFrame" id="aspectframe3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_columns">2</property>
+ <child>
+ <object class="GtkEntry" id="edt_password">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">●</property>
+ <property name="activates_default">True</property>
+ <property name="invisible_char_set">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="edt_username">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">20</property>
+ <property name="xalign">0</property>
+ <property name="ypad">10</property>
+ <property name="label" translatable="yes">Username</property>
+ <property name="justify">center</property>
+ <attributes>
+ <attribute name="weight" value="ultraheavy"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">20</property>
+ <property name="xalign">0</property>
+ <property name="ypad">10</property>
+ <property name="label" translatable="yes">Password</property>
+ <attributes>
+ <attribute name="weight" value="ultraheavy"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="btn_login">
+ <property name="label" translatable="yes">Login</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="lbl_info">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <property name="valign">end</property>
+ <property name="label" translatable="yes">VirtualBox Guest Additions</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="lblLogin">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Login</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAspectFrame" id="aspectframe2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">VirtualBox Guest Additions Auto-Logon</property>
+ <property name="justify">center</property>
+ <attributes>
+ <attribute name="weight" value="ultraheavy"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="lblInformation">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Information</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
+
diff --git a/src/VBox/Additions/linux/sharedfolders/.scm-settings b/src/VBox/Additions/linux/sharedfolders/.scm-settings
new file mode 100644
index 00000000..14d6e145
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/.scm-settings
@@ -0,0 +1,29 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for linux shared folders module.
+#
+
+#
+# Copyright (C) 2010-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+
+/*.c|/*.h: --no-convert-tabs
+/Makefile.module: --treat-as Makefile
+
+# MIT licence to make it easier to re-import code from the in-kernel version.
+/dirops.c: --license-mit
+/lnkops.c: --license-mit
+/regops.c: --license-mit
+/utils.c: --license-mit
+/vbsfmount.h: --license-mit
+/vfsmod.c: --license-mit
+/vfsmod.h: --license-mit
diff --git a/src/VBox/Additions/linux/sharedfolders/Makefile.kmk b/src/VBox/Additions/linux/sharedfolders/Makefile.kmk
new file mode 100644
index 00000000..d520f8df
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/Makefile.kmk
@@ -0,0 +1,45 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the vboxsf (linux shared folders module).
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+#
+# Populate FILES_VBOXSF_NOBIN
+#
+INSTALLS += vboxsf-src
+include $(PATH_SUB_CURRENT)/files_vboxsf
+vboxsf-src_INST = $(INST_ADDITIONS)src/vboxsf/
+vboxsf-src_SOURCES = \
+ $(subst $(DQUOTE),,$(FILES_VBOXSF_NOBIN))
+vboxsf-src_EXEC_SOURCES = \
+ $(subst $(DQUOTE),,$(FILES_VBOXSF_BIN))
+
+
+#
+# The mount util.
+#
+PROGRAMS += mount.vboxsf
+mount.vboxsf_TEMPLATE = NewVBoxGuestR3Exe
+mount.vboxsf_DEFS = _GNU_SOURCE
+mount.vboxsf_SOURCES = \
+ mount.vboxsf.c \
+ vbsfmount.c
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/linux/sharedfolders/Makefile.module b/src/VBox/Additions/linux/sharedfolders/Makefile.module
new file mode 100644
index 00000000..6c09d5f3
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/Makefile.module
@@ -0,0 +1,91 @@
+# $Id: Makefile.module $
+## @file
+# VBox Linux Shared Folders VFS Module Makefile.
+#
+# (For 2.6.x this file must be 'Makefile'!)
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+# Linux kbuild sets this to our source directory if we are called from there
+obj ?= $(CURDIR)
+include $(obj)/Makefile.include.header
+
+MOD_NAME = vboxsf
+MOD_OBJS = \
+ vfsmod.o \
+ dirops.o \
+ lnkops.o \
+ regops.o \
+ utils.o \
+ VBoxGuestR0LibHGCM.o \
+ VBoxGuestR0LibIdc.o \
+ VBoxGuestR0LibIdc-unix.o \
+ VBoxGuestR0LibSharedFolders.o
+ifeq ($(BUILD_TARGET_ARCH),x86)
+MOD_OBJS += \
+ divdi3.o \
+ moddi3.o \
+ udivdi3.o \
+ udivmoddi4.o \
+ umoddi3.o \
+ qdivrem.o
+endif
+
+MOD_INCL = \
+ $(addprefix -I$(KBUILD_EXTMOD),/ /include /r0drv/linux) \
+ $(addprefix -I$(KBUILD_EXTMOD)/vboxsf,/ /include /r0drv/linux)
+
+ifneq ($(wildcard $(KBUILD_EXTMOD)/vboxsf),)
+ MANGLING := $(KBUILD_EXTMOD)/vboxsf/include/VBox/VBoxGuestMangling.h
+else
+ MANGLING := $(KBUILD_EXTMOD)/include/VBox/VBoxGuestMangling.h
+endif
+
+MOD_DEFS = -DRT_OS_LINUX -DIN_RING0 -DIN_RT_R0 \
+ -DIN_SUP_R0 -DVBOX -DVBOX_WITH_HGCM -DIN_MODULE -DIN_GUEST_R0
+# our module does not export any symbol
+MOD_DEFS += -DRT_NO_EXPORT_SYMBOL
+ifeq ($(BUILD_TARGET_ARCH),amd64)
+ MOD_DEFS += -DRT_ARCH_AMD64 -DVBOX_WITH_64_BITS_GUESTS
+else
+ MOD_DEFS += -DRT_ARCH_X86
+endif
+
+ifeq ($(KERN_VERSION), 24)
+ MOD_CFLAGS =
+else
+ MOD_CFLAGS = -Wno-declaration-after-statement -fshort-wchar -include $(MANGLING) -fno-pie
+
+# special hack for Fedora Core 6 2.6.18 (fc6), rhel5 2.6.18 (el5),
+# ClarkConnect 4.3 (cc4) and ClarkConnect 5 (v5)
+ ifeq ($(KERNELRELEASE),)
+ MOD_EXTRA += $(foreach inc,$(KERN_INCL),\
+ $(if $(wildcard $(inc)/linux/utsrelease.h),\
+ $(if $(shell grep '"2.6.18.*fc6.*"' $(inc)/linux/utsrelease.h; \
+ grep '"2.6.18.*el5.*"' $(inc)/linux/utsrelease.h; \
+ grep '"2.6.18.*v5.*"' $(inc)/linux/utsrelease.h; \
+ grep '"2.6.18.*cc4.*"' $(inc)/linux/utsrelease.h),\
+ -DKERNEL_FC6,),))
+ else
+ MOD_EXTRA += $(if $(shell echo "$(KERNELRELEASE)"|grep '2.6.18.*fc6.*';\
+ echo "$(KERNELRELEASE)"|grep '2.6.18.*el5.*';\
+ echo "$(KERNELRELEASE)"|grep '2.6.18.*v5.*';\
+ echo "$(KERNELRELEASE)"|grep '2.6.18.*cc4.*'),\
+ -DKERNEL_FC6,)
+ endif
+endif
+
+MOD_CLEAN = . linux r0drv r0drv/linux
+
+include $(obj)/Makefile.include.footer
diff --git a/src/VBox/Additions/linux/sharedfolders/dirops.c b/src/VBox/Additions/linux/sharedfolders/dirops.c
new file mode 100644
index 00000000..9b14382f
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/dirops.c
@@ -0,0 +1,893 @@
+/* $Id: dirops.c $ */
+/** @file
+ * vboxsf - VBox Linux Shared Folders VFS, directory inode and file operations.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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.
+ */
+
+#include "vfsmod.h"
+#include <iprt/err.h>
+
+/**
+ * Open a directory. Read the complete content into a buffer.
+ *
+ * @param inode inode
+ * @param file file
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_dir_open(struct inode *inode, struct file *file)
+{
+ int rc;
+ int err;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_dir_info *sf_d;
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+ SHFLCREATEPARMS params;
+
+ TRACE();
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_i);
+
+ if (file->private_data) {
+ LogFunc(("sf_dir_open() called on already opened directory '%s'\n", sf_i->path->String.utf8));
+ return 0;
+ }
+
+ sf_d = sf_dir_info_alloc();
+ if (!sf_d) {
+ LogRelFunc(("could not allocate directory info for '%s'\n",
+ sf_i->path->String.utf8));
+ return -ENOMEM;
+ }
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ params.CreateFlags = 0
+ | SHFL_CF_DIRECTORY
+ | SHFL_CF_ACT_OPEN_IF_EXISTS
+ | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ;
+
+ LogFunc(("sf_dir_open(): calling VbglR0SfCreate, folder %s, flags %#x\n", sf_i->path->String.utf8, params.CreateFlags));
+ rc = VbglR0SfCreate(&client_handle, &sf_g->map, sf_i->path, &params);
+ if (RT_SUCCESS(rc)) {
+ if (params.Result == SHFL_FILE_EXISTS) {
+ err = sf_dir_read_all(sf_g, sf_i, sf_d, params.Handle);
+ if (!err)
+ file->private_data = sf_d;
+ } else
+ err = -ENOENT;
+
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("sf_dir_open(): VbglR0SfClose(%s) after err=%d failed rc=%Rrc\n", sf_i->path->String.utf8, err, rc));
+ } else
+ err = -EPERM;
+
+ if (err)
+ sf_dir_info_free(sf_d);
+
+ return err;
+}
+
+/**
+ * This is called when reference count of [file] goes to zero. Notify
+ * the host that it can free whatever is associated with this directory
+ * and deallocate our own internal buffers
+ *
+ * @param inode inode
+ * @param file file
+ * returns 0 on success, Linux error code otherwise
+ */
+static int sf_dir_release(struct inode *inode, struct file *file)
+{
+ TRACE();
+
+ if (file->private_data)
+ sf_dir_info_free(file->private_data);
+
+ return 0;
+}
+
+/**
+ * Translate RTFMODE into DT_xxx (in conjunction to rtDirType())
+ * @param fMode file mode
+ * returns d_type
+ */
+static int sf_get_d_type(RTFMODE fMode)
+{
+ int d_type;
+ switch (fMode & RTFS_TYPE_MASK) {
+ case RTFS_TYPE_FIFO:
+ d_type = DT_FIFO;
+ break;
+ case RTFS_TYPE_DEV_CHAR:
+ d_type = DT_CHR;
+ break;
+ case RTFS_TYPE_DIRECTORY:
+ d_type = DT_DIR;
+ break;
+ case RTFS_TYPE_DEV_BLOCK:
+ d_type = DT_BLK;
+ break;
+ case RTFS_TYPE_FILE:
+ d_type = DT_REG;
+ break;
+ case RTFS_TYPE_SYMLINK:
+ d_type = DT_LNK;
+ break;
+ case RTFS_TYPE_SOCKET:
+ d_type = DT_SOCK;
+ break;
+ case RTFS_TYPE_WHITEOUT:
+ d_type = DT_WHT;
+ break;
+ default:
+ d_type = DT_UNKNOWN;
+ break;
+ }
+ return d_type;
+}
+
+/**
+ * Extract element ([dir]->f_pos) from the directory [dir] into [d_name].
+ *
+ * @returns 0 for success, 1 for end reached, Linux error code otherwise.
+ */
+static int sf_getdent(struct file *dir, char d_name[NAME_MAX], int *d_type)
+{
+ loff_t cur;
+ struct sf_glob_info *sf_g;
+ struct sf_dir_info *sf_d;
+ struct sf_inode_info *sf_i;
+ struct inode *inode;
+ struct list_head *pos, *list;
+
+ TRACE();
+
+ inode = GET_F_DENTRY(dir)->d_inode;
+ sf_i = GET_INODE_INFO(inode);
+ sf_g = GET_GLOB_INFO(inode->i_sb);
+ sf_d = dir->private_data;
+
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_d);
+ BUG_ON(!sf_i);
+
+ if (sf_i->force_reread) {
+ int rc;
+ int err;
+ SHFLCREATEPARMS params;
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ params.CreateFlags = 0
+ | SHFL_CF_DIRECTORY
+ | SHFL_CF_ACT_OPEN_IF_EXISTS
+ | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ;
+
+ LogFunc(("sf_getdent: calling VbglR0SfCreate, folder %s, flags %#x\n", sf_i->path->String.utf8, params.CreateFlags));
+ rc = VbglR0SfCreate(&client_handle, &sf_g->map, sf_i->path,
+ &params);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfCreate(%s) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+ return -EPERM;
+ }
+
+ if (params.Result != SHFL_FILE_EXISTS) {
+ LogFunc(("directory %s does not exist\n",
+ sf_i->path->String.utf8));
+ sf_dir_info_free(sf_d);
+ return -ENOENT;
+ }
+
+ sf_dir_info_empty(sf_d);
+ err = sf_dir_read_all(sf_g, sf_i, sf_d, params.Handle);
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("VbglR0SfClose(%s) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+ if (err)
+ return err;
+
+ sf_i->force_reread = 0;
+ }
+
+ cur = 0;
+ list = &sf_d->info_list;
+ list_for_each(pos, list) {
+ struct sf_dir_buf *b;
+ SHFLDIRINFO *info;
+ loff_t i;
+
+ b = list_entry(pos, struct sf_dir_buf, head);
+ if (dir->f_pos >= cur + b->cEntries) {
+ cur += b->cEntries;
+ continue;
+ }
+
+ for (i = 0, info = b->buf; i < dir->f_pos - cur; ++i) {
+ size_t size;
+
+ size =
+ offsetof(SHFLDIRINFO,
+ name.String) + info->name.u16Size;
+ info = (SHFLDIRINFO *) ((uintptr_t) info + size);
+ }
+
+ *d_type = sf_get_d_type(info->Info.Attr.fMode);
+
+ return sf_nlscpy(sf_g, d_name, NAME_MAX,
+ info->name.String.utf8, info->name.u16Length);
+ }
+
+ return 1;
+}
+
+/**
+ * This is called when vfs wants to populate internal buffers with
+ * directory [dir]s contents. [opaque] is an argument to the
+ * [filldir]. [filldir] magically modifies it's argument - [opaque]
+ * and takes following additional arguments (which i in turn get from
+ * the host via sf_getdent):
+ *
+ * name : name of the entry (i must also supply it's length huh?)
+ * type : type of the entry (FILE | DIR | etc) (i ellect to use DT_UNKNOWN)
+ * pos : position/index of the entry
+ * ino : inode number of the entry (i fake those)
+ *
+ * [dir] contains:
+ * f_pos : cursor into the directory listing
+ * private_data : mean of communication with the host side
+ *
+ * Extract elements from the directory listing (incrementing f_pos
+ * along the way) and feed them to [filldir] until:
+ *
+ * a. there are no more entries (i.e. sf_getdent set done to 1)
+ * b. failure to compute fake inode number
+ * c. filldir returns an error (see comment on that)
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+static int sf_dir_iterate(struct file *dir, struct dir_context *ctx)
+#else
+static int sf_dir_read(struct file *dir, void *opaque, filldir_t filldir)
+#endif
+{
+ TRACE();
+ for (;;) {
+ int err;
+ ino_t fake_ino;
+ loff_t sanity;
+ char d_name[NAME_MAX];
+ int d_type = DT_UNKNOWN;
+
+ err = sf_getdent(dir, d_name, &d_type);
+ switch (err) {
+ case 1:
+ return 0;
+
+ case 0:
+ break;
+
+ case -1:
+ default:
+ /* skip erroneous entry and proceed */
+ LogFunc(("sf_getdent error %d\n", err));
+ dir->f_pos += 1;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ ctx->pos += 1;
+#endif
+ continue;
+ }
+
+ /* d_name now contains a valid entry name */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ sanity = ctx->pos + 0xbeef;
+#else
+ sanity = dir->f_pos + 0xbeef;
+#endif
+ fake_ino = sanity;
+ if (sanity - fake_ino) {
+ LogRelFunc(("can not compute ino\n"));
+ return -EINVAL;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ if (!dir_emit(ctx, d_name, strlen(d_name), fake_ino, d_type)) {
+ LogFunc(("dir_emit failed\n"));
+ return 0;
+ }
+#else
+ err =
+ filldir(opaque, d_name, strlen(d_name), dir->f_pos,
+ fake_ino, d_type);
+ if (err) {
+ LogFunc(("filldir returned error %d\n", err));
+ /* Rely on the fact that filldir returns error
+ only when it runs out of space in opaque */
+ return 0;
+ }
+#endif
+
+ dir->f_pos += 1;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ ctx->pos += 1;
+#endif
+ }
+
+ BUG();
+}
+
+struct file_operations sf_dir_fops = {
+ .open = sf_dir_open,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ .iterate = sf_dir_iterate,
+#else
+ .readdir = sf_dir_read,
+#endif
+ .release = sf_dir_release,
+ .read = generic_read_dir
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+ , .llseek = generic_file_llseek
+#endif
+};
+
+/* iops */
+
+/**
+ * This is called when vfs failed to locate dentry in the cache. The
+ * job of this function is to allocate inode and link it to dentry.
+ * [dentry] contains the name to be looked in the [parent] directory.
+ * Failure to locate the name is not a "hard" error, in this case NULL
+ * inode is added to [dentry] and vfs should proceed trying to create
+ * the entry via other means. NULL(or "positive" pointer) ought to be
+ * returned in case of success and "negative" pointer on error
+ */
+static struct dentry *sf_lookup(struct inode *parent, struct dentry *dentry
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ , unsigned int flags
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ , struct nameidata *nd
+#endif
+ )
+{
+ int err;
+ struct sf_inode_info *sf_i, *sf_new_i;
+ struct sf_glob_info *sf_g;
+ SHFLSTRING *path;
+ struct inode *inode;
+ ino_t ino;
+ SHFLFSOBJINFO fsinfo;
+
+ TRACE();
+ sf_g = GET_GLOB_INFO(parent->i_sb);
+ sf_i = GET_INODE_INFO(parent);
+
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_i);
+
+ err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path);
+ if (err)
+ goto fail0;
+
+ err = sf_stat(__func__, sf_g, path, &fsinfo, 1);
+ if (err) {
+ if (err == -ENOENT) {
+ /* -ENOENT: add NULL inode to dentry so it later can be
+ created via call to create/mkdir/open */
+ kfree(path);
+ inode = NULL;
+ } else
+ goto fail1;
+ } else {
+ sf_new_i = kmalloc(sizeof(*sf_new_i), GFP_KERNEL);
+ if (!sf_new_i) {
+ LogRelFunc(("could not allocate memory for new inode info\n"));
+ err = -ENOMEM;
+ goto fail1;
+ }
+ sf_new_i->handle = SHFL_HANDLE_NIL;
+ sf_new_i->force_reread = 0;
+
+ ino = iunique(parent->i_sb, 1);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
+ inode = iget_locked(parent->i_sb, ino);
+#else
+ inode = iget(parent->i_sb, ino);
+#endif
+ if (!inode) {
+ LogFunc(("iget failed\n"));
+ err = -ENOMEM; /* XXX: ??? */
+ goto fail2;
+ }
+
+ SET_INODE_INFO(inode, sf_new_i);
+ sf_init_inode(sf_g, inode, &fsinfo);
+ sf_new_i->path = path;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
+ unlock_new_inode(inode);
+#endif
+ }
+
+ sf_i->force_restat = 0;
+ dentry->d_time = jiffies;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
+ d_set_d_op(dentry, &sf_dentry_ops);
+#else
+ dentry->d_op = &sf_dentry_ops;
+#endif
+ d_add(dentry, inode);
+ return NULL;
+
+ fail2:
+ kfree(sf_new_i);
+
+ fail1:
+ kfree(path);
+
+ fail0:
+ return ERR_PTR(err);
+}
+
+/**
+ * This should allocate memory for sf_inode_info, compute a unique inode
+ * number, get an inode from vfs, initialize inode info, instantiate
+ * dentry.
+ *
+ * @param parent inode entry of the directory
+ * @param dentry directory cache entry
+ * @param path path name
+ * @param info file information
+ * @param handle handle
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_instantiate(struct inode *parent, struct dentry *dentry,
+ SHFLSTRING * path, PSHFLFSOBJINFO info,
+ SHFLHANDLE handle)
+{
+ int err;
+ ino_t ino;
+ struct inode *inode;
+ struct sf_inode_info *sf_new_i;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(parent->i_sb);
+
+ TRACE();
+ BUG_ON(!sf_g);
+
+ sf_new_i = kmalloc(sizeof(*sf_new_i), GFP_KERNEL);
+ if (!sf_new_i) {
+ LogRelFunc(("could not allocate inode info.\n"));
+ err = -ENOMEM;
+ goto fail0;
+ }
+
+ ino = iunique(parent->i_sb, 1);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
+ inode = iget_locked(parent->i_sb, ino);
+#else
+ inode = iget(parent->i_sb, ino);
+#endif
+ if (!inode) {
+ LogFunc(("iget failed\n"));
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ sf_init_inode(sf_g, inode, info);
+ sf_new_i->path = path;
+ SET_INODE_INFO(inode, sf_new_i);
+ sf_new_i->force_restat = 1;
+ sf_new_i->force_reread = 0;
+
+ d_instantiate(dentry, inode);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
+ unlock_new_inode(inode);
+#endif
+
+ /* Store this handle if we leave the handle open. */
+ sf_new_i->handle = handle;
+ return 0;
+
+ fail1:
+ kfree(sf_new_i);
+
+ fail0:
+ return err;
+
+}
+
+/**
+ * Create a new regular file / directory.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @param mode file mode
+ * @param fDirectory true if directory, false otherwise
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_create_aux(struct inode *parent, struct dentry *dentry,
+ umode_t mode, int fDirectory)
+{
+ int rc, err;
+ SHFLCREATEPARMS params;
+ SHFLSTRING *path;
+ struct sf_inode_info *sf_i = GET_INODE_INFO(parent);
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(parent->i_sb);
+
+ TRACE();
+ BUG_ON(!sf_i);
+ BUG_ON(!sf_g);
+
+ err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path);
+ if (err)
+ goto fail0;
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ params.CreateFlags = 0
+ | SHFL_CF_ACT_CREATE_IF_NEW
+ | SHFL_CF_ACT_FAIL_IF_EXISTS
+ | SHFL_CF_ACCESS_READWRITE | (fDirectory ? SHFL_CF_DIRECTORY : 0);
+ params.Info.Attr.fMode = 0
+ | (fDirectory ? RTFS_TYPE_DIRECTORY : RTFS_TYPE_FILE)
+ | (mode & S_IRWXUGO);
+ params.Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+
+ LogFunc(("sf_create_aux: calling VbglR0SfCreate, folder %s, flags %#x\n", path->String.utf8, params.CreateFlags));
+ rc = VbglR0SfCreate(&client_handle, &sf_g->map, path, &params);
+ if (RT_FAILURE(rc)) {
+ if (rc == VERR_WRITE_PROTECT) {
+ err = -EROFS;
+ goto fail1;
+ }
+ err = -EPROTO;
+ LogFunc(("(%d): VbglR0SfCreate(%s) failed rc=%Rrc\n",
+ fDirectory, sf_i->path->String.utf8, rc));
+ goto fail1;
+ }
+
+ if (params.Result != SHFL_FILE_CREATED) {
+ err = -EPERM;
+ LogFunc(("(%d): could not create file %s result=%d\n",
+ fDirectory, sf_i->path->String.utf8, params.Result));
+ goto fail1;
+ }
+
+ err = sf_instantiate(parent, dentry, path, &params.Info,
+ fDirectory ? SHFL_HANDLE_NIL : params.Handle);
+ if (err) {
+ LogFunc(("(%d): could not instantiate dentry for %s err=%d\n",
+ fDirectory, sf_i->path->String.utf8, err));
+ goto fail2;
+ }
+
+ /*
+ * Don't close this handle right now. We assume that the same file is
+ * opened with sf_reg_open() and later closed with sf_reg_close(). Save
+ * the handle in between. Does not apply to directories. True?
+ */
+ if (fDirectory) {
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("(%d): VbglR0SfClose failed rc=%Rrc\n",
+ fDirectory, rc));
+ }
+
+ sf_i->force_restat = 1;
+ return 0;
+
+ fail2:
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("(%d): VbglR0SfClose failed rc=%Rrc\n", fDirectory,
+ rc));
+
+ fail1:
+ kfree(path);
+
+ fail0:
+ return err;
+}
+
+/**
+ * Create a new regular file.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @param mode file mode
+ * @param excl Possible O_EXCL...
+ * @returns 0 on success, Linux error code otherwise
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(DOXYGEN_RUNNING)
+static int sf_create(struct inode *parent, struct dentry *dentry, umode_t mode,
+ bool excl)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
+static int sf_create(struct inode *parent, struct dentry *dentry, umode_t mode,
+ struct nameidata *nd)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+static int sf_create(struct inode *parent, struct dentry *dentry, int mode,
+ struct nameidata *nd)
+#else
+static int sf_create(struct inode *parent, struct dentry *dentry, int mode)
+#endif
+{
+ TRACE();
+ return sf_create_aux(parent, dentry, mode, 0);
+}
+
+/**
+ * Create a new directory.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @param mode file mode
+ * @returns 0 on success, Linux error code otherwise
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
+static int sf_mkdir(struct inode *parent, struct dentry *dentry, umode_t mode)
+#else
+static int sf_mkdir(struct inode *parent, struct dentry *dentry, int mode)
+#endif
+{
+ TRACE();
+ return sf_create_aux(parent, dentry, mode, 1);
+}
+
+/**
+ * Remove a regular file / directory.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @param fDirectory true if directory, false otherwise
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_unlink_aux(struct inode *parent, struct dentry *dentry,
+ int fDirectory)
+{
+ int rc, err;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(parent->i_sb);
+ struct sf_inode_info *sf_i = GET_INODE_INFO(parent);
+ SHFLSTRING *path;
+ uint32_t fFlags;
+
+ TRACE();
+ BUG_ON(!sf_g);
+
+ err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path);
+ if (err)
+ goto fail0;
+
+ fFlags = fDirectory ? SHFL_REMOVE_DIR : SHFL_REMOVE_FILE;
+ if (dentry->d_inode && ((dentry->d_inode->i_mode & S_IFLNK) == S_IFLNK))
+ fFlags |= SHFL_REMOVE_SYMLINK;
+ rc = VbglR0SfRemove(&client_handle, &sf_g->map, path, fFlags);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("(%d): VbglR0SfRemove(%s) failed rc=%Rrc\n",
+ fDirectory, path->String.utf8, rc));
+ err = -RTErrConvertToErrno(rc);
+ goto fail1;
+ }
+
+ /* directory access/change time changed */
+ sf_i->force_restat = 1;
+ /* directory content changed */
+ sf_i->force_reread = 1;
+
+ err = 0;
+
+ fail1:
+ kfree(path);
+
+ fail0:
+ return err;
+}
+
+/**
+ * Remove a regular file.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_unlink(struct inode *parent, struct dentry *dentry)
+{
+ TRACE();
+ return sf_unlink_aux(parent, dentry, 0);
+}
+
+/**
+ * Remove a directory.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_rmdir(struct inode *parent, struct dentry *dentry)
+{
+ TRACE();
+ return sf_unlink_aux(parent, dentry, 1);
+}
+
+/**
+ * Rename a regular file / directory.
+ *
+ * @param old_parent inode of the old parent directory
+ * @param old_dentry old directory cache entry
+ * @param new_parent inode of the new parent directory
+ * @param new_dentry new directory cache entry
+ * @param flags flags
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_rename(struct inode *old_parent, struct dentry *old_dentry,
+ struct inode *new_parent, struct dentry *new_dentry
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
+ , unsigned flags
+#endif
+ )
+{
+ int err = 0, rc = VINF_SUCCESS;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(old_parent->i_sb);
+
+ TRACE();
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
+ if (flags) {
+ LogFunc(("rename with flags=%x\n", flags));
+ return -EINVAL;
+ }
+#endif
+
+ if (sf_g != GET_GLOB_INFO(new_parent->i_sb)) {
+ LogFunc(("rename with different roots\n"));
+ err = -EINVAL;
+ } else {
+ struct sf_inode_info *sf_old_i = GET_INODE_INFO(old_parent);
+ struct sf_inode_info *sf_new_i = GET_INODE_INFO(new_parent);
+ /* As we save the relative path inside the inode structure, we need to change
+ this if the rename is successful. */
+ struct sf_inode_info *sf_file_i =
+ GET_INODE_INFO(old_dentry->d_inode);
+ SHFLSTRING *old_path;
+ SHFLSTRING *new_path;
+
+ BUG_ON(!sf_old_i);
+ BUG_ON(!sf_new_i);
+ BUG_ON(!sf_file_i);
+
+ old_path = sf_file_i->path;
+ err = sf_path_from_dentry(__func__, sf_g, sf_new_i,
+ new_dentry, &new_path);
+ if (err)
+ LogFunc(("failed to create new path\n"));
+ else {
+ int fDir =
+ ((old_dentry->d_inode->i_mode & S_IFDIR) != 0);
+
+ rc = VbglR0SfRename(&client_handle, &sf_g->map,
+ old_path, new_path,
+ fDir ? 0 : SHFL_RENAME_FILE |
+ SHFL_RENAME_REPLACE_IF_EXISTS);
+ if (RT_SUCCESS(rc)) {
+ kfree(old_path);
+ sf_new_i->force_restat = 1;
+ sf_old_i->force_restat = 1; /* XXX: needed? */
+ /* Set the new relative path in the inode. */
+ sf_file_i->path = new_path;
+ } else {
+ LogFunc(("VbglR0SfRename failed rc=%Rrc\n",
+ rc));
+ err = -RTErrConvertToErrno(rc);
+ kfree(new_path);
+ }
+ }
+ }
+ return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+static int sf_symlink(struct inode *parent, struct dentry *dentry,
+ const char *symname)
+{
+ int err;
+ int rc;
+ struct sf_inode_info *sf_i;
+ struct sf_glob_info *sf_g;
+ SHFLSTRING *path, *ssymname;
+ SHFLFSOBJINFO info;
+ int symname_len = strlen(symname) + 1;
+
+ TRACE();
+ sf_g = GET_GLOB_INFO(parent->i_sb);
+ sf_i = GET_INODE_INFO(parent);
+
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_i);
+
+ err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path);
+ if (err)
+ goto fail0;
+
+ ssymname =
+ kmalloc(offsetof(SHFLSTRING, String.utf8) + symname_len,
+ GFP_KERNEL);
+ if (!ssymname) {
+ LogRelFunc(("kmalloc failed, caller=sf_symlink\n"));
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ ssymname->u16Length = symname_len - 1;
+ ssymname->u16Size = symname_len;
+ memcpy(ssymname->String.utf8, symname, symname_len);
+
+ rc = VbglR0SfSymlink(&client_handle, &sf_g->map, path, ssymname, &info);
+ kfree(ssymname);
+
+ if (RT_FAILURE(rc)) {
+ if (rc == VERR_WRITE_PROTECT) {
+ err = -EROFS;
+ goto fail1;
+ }
+ LogFunc(("VbglR0SfSymlink(%s) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+ err = -EPROTO;
+ goto fail1;
+ }
+
+ err = sf_instantiate(parent, dentry, path, &info, SHFL_HANDLE_NIL);
+ if (err) {
+ LogFunc(("could not instantiate dentry for %s err=%d\n",
+ sf_i->path->String.utf8, err));
+ goto fail1;
+ }
+
+ sf_i->force_restat = 1;
+ return 0;
+
+ fail1:
+ kfree(path);
+ fail0:
+ return err;
+}
+#endif /* LINUX_VERSION_CODE >= 2.6.0 */
+
+struct inode_operations sf_dir_iops = {
+ .lookup = sf_lookup,
+ .create = sf_create,
+ .mkdir = sf_mkdir,
+ .rmdir = sf_rmdir,
+ .unlink = sf_unlink,
+ .rename = sf_rename,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+ .revalidate = sf_inode_revalidate
+#else
+ .getattr = sf_getattr,
+ .setattr = sf_setattr,
+ .symlink = sf_symlink
+#endif
+};
diff --git a/src/VBox/Additions/linux/sharedfolders/files_vboxsf b/src/VBox/Additions/linux/sharedfolders/files_vboxsf
new file mode 100755
index 00000000..9660f366
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/files_vboxsf
@@ -0,0 +1,89 @@
+#!/bin/sh
+# $Id: files_vboxsf $
+## @file
+# Shared file between Makefile.kmk and export_modules.sh.
+#
+
+#
+# Copyright (C) 2007-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+FILES_VBOXSF_NOBIN=" \
+ ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \
+ ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \
+ ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \
+ ${PATH_ROOT}/include/iprt/asm-amd64-x86.h=>include/iprt/asm-amd64-x86.h \
+ ${PATH_ROOT}/include/iprt/asm-math.h=>include/iprt/asm-math.h \
+ ${PATH_ROOT}/include/iprt/assert.h=>include/iprt/assert.h \
+ ${PATH_ROOT}/include/iprt/assertcompile.h=>include/iprt/assertcompile.h \
+ ${PATH_ROOT}/include/iprt/cdefs.h=>include/iprt/cdefs.h \
+ ${PATH_ROOT}/include/iprt/err.h=>include/iprt/err.h \
+ ${PATH_ROOT}/include/iprt/errcore.h=>include/iprt/errcore.h \
+ ${PATH_ROOT}/include/iprt/fs.h=>include/iprt/fs.h \
+ ${PATH_ROOT}/include/iprt/latin1.h=>include/iprt/latin1.h \
+ ${PATH_ROOT}/include/iprt/log.h=>include/iprt/log.h \
+ ${PATH_ROOT}/include/iprt/mangling.h=>include/iprt/mangling.h \
+ ${PATH_ROOT}/include/iprt/mem.h=>include/iprt/mem.h \
+ ${PATH_ROOT}/include/iprt/param.h=>include/iprt/param.h \
+ ${PATH_ROOT}/include/iprt/path.h=>include/iprt/path.h \
+ ${PATH_ROOT}/include/iprt/semaphore.h=>include/iprt/semaphore.h \
+ ${PATH_ROOT}/include/iprt/stdarg.h=>include/iprt/stdarg.h \
+ ${PATH_ROOT}/include/iprt/stdint.h=>include/iprt/stdint.h \
+ ${PATH_ROOT}/include/iprt/string.h=>include/iprt/string.h \
+ ${PATH_ROOT}/include/iprt/time.h=>include/iprt/time.h \
+ ${PATH_ROOT}/include/iprt/types.h=>include/iprt/types.h \
+ ${PATH_ROOT}/include/iprt/uint64.h=>include/iprt/uint64.h \
+ ${PATH_ROOT}/include/iprt/uni.h=>include/iprt/uni.h \
+ ${PATH_ROOT}/include/iprt/utf16.h=>include/iprt/utf16.h \
+ ${PATH_ROOT}/include/VBox/cdefs.h=>include/VBox/cdefs.h \
+ ${PATH_ROOT}/include/VBox/err.h=>include/VBox/err.h \
+ ${PATH_ROOT}/include/VBox/log.h=>include/VBox/log.h \
+ ${PATH_ROOT}/include/VBox/ostypes.h=>include/VBox/ostypes.h \
+ ${PATH_ROOT}/include/VBox/param.h=>include/VBox/param.h \
+ ${PATH_ROOT}/include/VBox/shflsvc.h=>include/VBox/shflsvc.h \
+ ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \
+ ${PATH_ROOT}/include/VBox/VBoxGuest.h=>include/VBox/VBoxGuest.h \
+ ${PATH_ROOT}/include/VBox/VBoxGuestCoreTypes.h=>include/VBox/VBoxGuestCoreTypes.h \
+ ${PATH_ROOT}/include/VBox/VBoxGuestLib.h=>include/VBox/VBoxGuestLib.h \
+ ${PATH_ROOT}/include/VBox/VBoxGuestLibSharedFolders.h=>include/VBox/VBoxGuestLibSharedFolders.h \
+ ${PATH_ROOT}/include/VBox/VBoxGuestMangling.h=>include/VBox/VBoxGuestMangling.h \
+ ${PATH_ROOT}/include/VBox/VMMDev.h=>include/VBox/VMMDev.h \
+ ${PATH_ROOT}/include/VBox/VMMDevCoreTypes.h=>include/VBox/VMMDevCoreTypes.h \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h=>VBoxGuestR0LibInternal.h \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp=>VBoxGuestR0LibHGCM.c \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc.cpp=>VBoxGuestR0LibIdc.c \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-unix.cpp=>VBoxGuestR0LibIdc-unix.c \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c=>VBoxGuestR0LibSharedFolders.c \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile.include.header=>Makefile.include.header \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile.include.footer=>Makefile.include.footer \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/divdi3.c=>divdi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/moddi3.c=>moddi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/qdivrem.c=>qdivrem.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/quad.h=>quad.h \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivdi3.c=>udivdi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivmoddi4.c=>udivmoddi4.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/umoddi3.c=>umoddi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/the-linux-kernel.h=>r0drv/linux/the-linux-kernel.h \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/Makefile.module=>Makefile \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/dirops.c=>dirops.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/lnkops.c=>lnkops.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/regops.c=>regops.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/utils.c=>utils.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/vbsfmount.h=>vbsfmount.h \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/vfsmod.c=>vfsmod.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/vfsmod.h=>vfsmod.h \
+ ${PATH_OUT}/version-generated.h=>version-generated.h \
+ ${PATH_OUT}/revision-generated.h=>revision-generated.h \
+ ${PATH_OUT}/product-generated.h=>product-generated.h \
+"
+
+FILES_VBOXSF_BIN=" \
+"
diff --git a/src/VBox/Additions/linux/sharedfolders/lnkops.c b/src/VBox/Additions/linux/sharedfolders/lnkops.c
new file mode 100644
index 00000000..d735b677
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/lnkops.c
@@ -0,0 +1,119 @@
+/* $Id: lnkops.c $ */
+/** @file
+ * vboxsf - VBox Linux Shared Folders VFS, operations for symbolic links.
+ */
+
+/*
+ * Copyright (C) 2010-2019 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.
+ */
+
+#include "vfsmod.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+
+# if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+static const char *sf_follow_link(struct dentry *dentry, void **cookie)
+# else
+static void *sf_follow_link(struct dentry *dentry, struct nameidata *nd)
+# endif
+{
+ struct inode *inode = dentry->d_inode;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+ int error = -ENOMEM;
+ char *path = (char *)get_zeroed_page(GFP_KERNEL);
+ int rc;
+
+ if (path) {
+ error = 0;
+ rc = VbglR0SfReadLink(&client_handle, &sf_g->map, sf_i->path,
+ PATH_MAX, path);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfReadLink failed, caller=%s, rc=%Rrc\n", __func__, rc));
+ free_page((unsigned long)path);
+ error = -EPROTO;
+ }
+ }
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+ return error ? ERR_PTR(error) : (*cookie = path);
+# else
+ nd_set_link(nd, error ? ERR_PTR(error) : path);
+ return NULL;
+# endif
+}
+
+# if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+static void sf_put_link(struct dentry *dentry, struct nameidata *nd,
+ void *cookie)
+{
+ char *page = nd_get_link(nd);
+ if (!IS_ERR(page))
+ free_page((unsigned long)page);
+}
+# endif
+
+# else /* LINUX_VERSION_CODE >= 4.5.0 */
+static const char *sf_get_link(struct dentry *dentry, struct inode *inode,
+ struct delayed_call *done)
+{
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+ char *path;
+ int rc;
+
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+ path = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!path)
+ return ERR_PTR(-ENOMEM);
+ rc = VbglR0SfReadLink(&client_handle, &sf_g->map, sf_i->path, PATH_MAX,
+ path);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfReadLink failed, caller=%s, rc=%Rrc\n",
+ __func__, rc));
+ kfree(path);
+ return ERR_PTR(-EPROTO);
+ }
+ set_delayed_call(done, kfree_link, path);
+ return path;
+}
+# endif /* LINUX_VERSION_CODE >= 4.5.0 */
+
+struct inode_operations sf_lnk_iops = {
+# if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
+ .readlink = generic_readlink,
+# endif
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
+ .get_link = sf_get_link
+# elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+ .follow_link = sf_follow_link,
+ .put_link = free_page_put_link,
+# else
+ .follow_link = sf_follow_link,
+ .put_link = sf_put_link
+# endif
+};
+
+#endif /* LINUX_VERSION_CODE >= 2.6.0 */
diff --git a/src/VBox/Additions/linux/sharedfolders/mount.vboxsf.c b/src/VBox/Additions/linux/sharedfolders/mount.vboxsf.c
new file mode 100644
index 00000000..7392b047
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/mount.vboxsf.c
@@ -0,0 +1,538 @@
+/* $Id: mount.vboxsf.c $ */
+/** @file
+ * VirtualBox Guest Additions for Linux - mount(8) helper.
+ *
+ * Parses options provided by mount (or user directly)
+ * Packs them into struct vbsfmount and passes to mount(2)
+ * Optionally adds entries to mtab
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+/* #define DEBUG */
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <mntent.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <mntent.h>
+#include <limits.h>
+#include <iconv.h>
+
+#include "vbsfmount.h"
+
+#include <iprt/assert.h>
+
+
+#define PANIC_ATTR __attribute ((noreturn, __format__ (__printf__, 1, 2)))
+
+static void PANIC_ATTR
+panic(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+static void PANIC_ATTR
+panic_err(const char *fmt, ...)
+{
+ va_list ap;
+ int errno_code = errno;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, ": %s\n", strerror(errno_code));
+ exit(EXIT_FAILURE);
+}
+
+static int
+safe_atoi(const char *s, size_t size, int base)
+{
+ char *endptr;
+ long long int val = strtoll(s, &endptr, base);
+
+ if (val < INT_MIN || val > INT_MAX || endptr < s + size)
+ {
+ errno = ERANGE;
+ panic_err("could not convert %.*s to integer, result = %d",
+ (int)size, s, (int) val);
+ }
+ return (int)val;
+}
+
+static void
+process_mount_opts(const char *s, struct vbsf_mount_opts *opts)
+{
+ const char *next = s;
+ size_t len;
+ typedef enum handler_opt
+ {
+ HORW,
+ HORO,
+ HOUID,
+ HOGID,
+ HOTTL,
+ HODMODE,
+ HOFMODE,
+ HOUMASK,
+ HODMASK,
+ HOFMASK,
+ HOIOCHARSET,
+ HOCONVERTCP,
+ HONOEXEC,
+ HOEXEC,
+ HONODEV,
+ HODEV,
+ HONOSUID,
+ HOSUID,
+ HOREMOUNT,
+ HONOAUTO,
+ HONIGNORE
+ } handler_opt;
+ struct
+ {
+ const char *name;
+ handler_opt opt;
+ int has_arg;
+ const char *desc;
+ } handlers[] =
+ {
+ {"rw", HORW, 0, "mount read write (default)"},
+ {"ro", HORO, 0, "mount read only"},
+ {"uid", HOUID, 1, "default file owner user id"},
+ {"gid", HOGID, 1, "default file owner group id"},
+ {"ttl", HOTTL, 1, "time to live for dentry"},
+ {"iocharset", HOIOCHARSET, 1, "i/o charset (default utf8)"},
+ {"convertcp", HOCONVERTCP, 1, "convert share name from given charset to utf8"},
+ {"dmode", HODMODE, 1, "mode of all directories"},
+ {"fmode", HOFMODE, 1, "mode of all regular files"},
+ {"umask", HOUMASK, 1, "umask of directories and regular files"},
+ {"dmask", HODMASK, 1, "umask of directories"},
+ {"fmask", HOFMASK, 1, "umask of regular files"},
+ {"noexec", HONOEXEC, 0, 0 }, /* don't document these options directly here */
+ {"exec", HOEXEC, 0, 0 }, /* as they are well known and described in the */
+ {"nodev", HONODEV, 0, 0 }, /* usual manpages */
+ {"dev", HODEV, 0, 0 },
+ {"nosuid", HONOSUID, 0, 0 },
+ {"suid", HOSUID, 0, 0 },
+ {"remount", HOREMOUNT, 0, 0 },
+ {"noauto", HONOAUTO, 0, 0 },
+ {"_netdev", HONIGNORE, 0, 0 },
+ {NULL, 0, 0, NULL}
+ }, *handler;
+
+ while (next)
+ {
+ const char *val;
+ size_t key_len, val_len;
+
+ s = next;
+ next = strchr(s, ',');
+ if (!next)
+ {
+ len = strlen(s);
+ }
+ else
+ {
+ len = next - s;
+ next += 1;
+ if (!*next)
+ next = 0;
+ }
+
+ val = NULL;
+ val_len = 0;
+ for (key_len = 0; key_len < len; ++key_len)
+ {
+ if (s[key_len] == '=')
+ {
+ if (key_len + 1 < len)
+ {
+ val = s + key_len + 1;
+ val_len = len - key_len - 1;
+ }
+ break;
+ }
+ }
+
+ for (handler = handlers; handler->name; ++handler)
+ {
+ size_t j;
+ for (j = 0; j < key_len && handler->name[j] == s[j]; ++j)
+ ;
+
+ if (j == key_len && !handler->name[j])
+ {
+ if (handler->has_arg)
+ {
+ if (!(val && *val))
+ {
+ panic("%.*s requires an argument (i.e. %.*s=<arg>)\n",
+ (int)len, s, (int)len, s);
+ }
+ }
+
+ switch(handler->opt)
+ {
+ case HORW:
+ opts->ronly = 0;
+ break;
+ case HORO:
+ opts->ronly = 1;
+ break;
+ case HONOEXEC:
+ opts->noexec = 1;
+ break;
+ case HOEXEC:
+ opts->noexec = 0;
+ break;
+ case HONODEV:
+ opts->nodev = 1;
+ break;
+ case HODEV:
+ opts->nodev = 0;
+ break;
+ case HONOSUID:
+ opts->nosuid = 1;
+ break;
+ case HOSUID:
+ opts->nosuid = 0;
+ break;
+ case HOREMOUNT:
+ opts->remount = 1;
+ break;
+ case HOUID:
+ /** @todo convert string to id. */
+ opts->uid = safe_atoi(val, val_len, 10);
+ break;
+ case HOGID:
+ /** @todo convert string to id. */
+ opts->gid = safe_atoi(val, val_len, 10);
+ break;
+ case HOTTL:
+ opts->ttl = safe_atoi(val, val_len, 10);
+ break;
+ case HODMODE:
+ opts->dmode = safe_atoi(val, val_len, 8);
+ break;
+ case HOFMODE:
+ opts->fmode = safe_atoi(val, val_len, 8);
+ break;
+ case HOUMASK:
+ opts->dmask = opts->fmask = safe_atoi(val, val_len, 8);
+ break;
+ case HODMASK:
+ opts->dmask = safe_atoi(val, val_len, 8);
+ break;
+ case HOFMASK:
+ opts->fmask = safe_atoi(val, val_len, 8);
+ break;
+ case HOIOCHARSET:
+ if (val_len + 1 > sizeof(opts->nls_name))
+ {
+ panic("iocharset name too long\n");
+ }
+ memcpy(opts->nls_name, val, val_len);
+ opts->nls_name[val_len] = 0;
+ break;
+ case HOCONVERTCP:
+ opts->convertcp = malloc(val_len + 1);
+ if (!opts->convertcp)
+ {
+ panic_err("could not allocate memory");
+ }
+ memcpy(opts->convertcp, val, val_len);
+ opts->convertcp[val_len] = 0;
+ break;
+ case HONOAUTO:
+ case HONIGNORE:
+ break;
+ }
+ break;
+ }
+ continue;
+ }
+
+ if ( !handler->name
+ && !opts->sloppy)
+ {
+ fprintf(stderr, "unknown mount option `%.*s'\n", (int)len, s);
+ fprintf(stderr, "valid options:\n");
+
+ for (handler = handlers; handler->name; ++handler)
+ {
+ if (handler->desc)
+ fprintf(stderr, " %-10s%s %s\n", handler->name,
+ handler->has_arg ? "=<arg>" : "", handler->desc);
+ }
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+static void
+convertcp(char *in_codeset, char *host_name, struct vbsf_mount_info_new *info)
+{
+ char *i = host_name;
+ char *o = info->name;
+ size_t ib = strlen(host_name);
+ size_t ob = sizeof(info->name) - 1;
+ iconv_t cd;
+
+ cd = iconv_open("UTF-8", in_codeset);
+ if (cd == (iconv_t) -1)
+ {
+ panic_err("could not convert share name, iconv_open `%s' failed",
+ in_codeset);
+ }
+
+ while (ib)
+ {
+ size_t c = iconv(cd, &i, &ib, &o, &ob);
+ if (c == (size_t) -1)
+ {
+ panic_err("could not convert share name(%s) at %d",
+ host_name, (int)(strlen (host_name) - ib));
+ }
+ }
+ *o = 0;
+}
+
+
+/**
+ * Print out a usage message and exit.
+ *
+ * @returns 1
+ * @param argv0 The name of the application
+ */
+static int usage(char *argv0)
+{
+ printf("Usage: %s [OPTIONS] NAME MOUNTPOINT\n"
+ "Mount the VirtualBox shared folder NAME from the host system to MOUNTPOINT.\n"
+ "\n"
+ " -w mount the shared folder writable (the default)\n"
+ " -r mount the shared folder read-only\n"
+ " -n do not create an mtab entry\n"
+ " -s sloppy parsing, ignore unrecognized mount options\n"
+ " -o OPTION[,OPTION...] use the mount options specified\n"
+ "\n", argv0);
+ printf("Available mount options are:\n"
+ " rw mount writable (the default)\n"
+ " ro mount read only\n"
+ " uid=UID set the default file owner user id to UID\n"
+ " gid=GID set the default file owner group id to GID\n"
+ " ttl=TTL set the \"time to live\" to TID for the dentry\n");
+ printf(" dmode=MODE override the mode of all directories to (octal) MODE\n"
+ " fmode=MODE override the mode of all regular files to (octal) MODE\n"
+ " umask=UMASK set the umask to (octal) UMASK\n");
+ printf(" dmask=UMASK set the umask applied to directories only\n"
+ " fmask=UMASK set the umask applied to regular files only\n"
+ " iocharset CHARSET use the character set CHARSET for I/O operations\n"
+ " (default set is utf8)\n"
+ " convertcp CHARSET convert the folder name from CHARSET to utf8\n"
+ "\n");
+ printf("Less common used options:\n"
+ " noexec,exec,nodev,dev,nosuid,suid\n");
+ return EXIT_FAILURE;
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int err;
+ int nomtab = 0;
+ unsigned long flags = MS_NODEV;
+ char *host_name;
+ char *mount_point;
+ struct vbsf_mount_info_new mntinf;
+ struct vbsf_mount_opts opts =
+ {
+ 0, /* uid */
+ 0, /* gid */
+ 0, /* ttl */
+ ~0U, /* dmode */
+ ~0U, /* fmode*/
+ 0, /* dmask */
+ 0, /* fmask */
+ 0, /* ronly */
+ 0, /* sloppy */
+ 0, /* noexec */
+ 0, /* nodev */
+ 0, /* nosuid */
+ 0, /* remount */
+ "\0", /* nls_name */
+ NULL, /* convertcp */
+ };
+ AssertCompile(sizeof(uid_t) == sizeof(int));
+ AssertCompile(sizeof(gid_t) == sizeof(int));
+
+ mntinf.nullchar = '\0';
+ mntinf.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
+ mntinf.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
+ mntinf.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
+ mntinf.length = sizeof(mntinf);
+ mntinf.tag[0] = '\0';
+
+ if (getuid())
+ panic("Only root can mount shared folders from the host.\n");
+
+ if (!argv[0])
+ argv[0] = "mount.vboxsf";
+
+ while ((c = getopt(argc, argv, "rwsno:h")) != -1)
+ {
+ switch (c)
+ {
+ default:
+ fprintf(stderr, "unknown option `%c:%#x'\n", c, c);
+ RT_FALL_THRU();
+ case '?':
+ case 'h':
+ return usage(argv[0]);
+
+ case 'r':
+ opts.ronly = 1;
+ break;
+
+ case 'w':
+ opts.ronly = 0;
+ break;
+
+ case 's':
+ opts.sloppy = 1;
+ break;
+
+ case 'o':
+ process_mount_opts(optarg, &opts);
+ break;
+
+ case 'n':
+ nomtab = 1;
+ break;
+ }
+ }
+
+ if (argc - optind < 2)
+ return usage(argv[0]);
+
+ host_name = argv[optind];
+ mount_point = argv[optind + 1];
+
+ if (opts.convertcp)
+ convertcp(opts.convertcp, host_name, &mntinf);
+ else
+ {
+ if (strlen(host_name) > MAX_HOST_NAME - 1)
+ panic("host name is too big\n");
+
+ strcpy(mntinf.name, host_name);
+ }
+
+ if (strlen(opts.nls_name) > MAX_NLS_NAME - 1)
+ panic("%s: the character set name for I/O is too long.\n", argv[0]);
+
+ strcpy(mntinf.nls_name, opts.nls_name);
+
+ if (opts.ronly)
+ flags |= MS_RDONLY;
+ if (opts.noexec)
+ flags |= MS_NOEXEC;
+ if (opts.nodev)
+ flags |= MS_NODEV;
+ if (opts.remount)
+ flags |= MS_REMOUNT;
+
+ mntinf.uid = opts.uid;
+ mntinf.gid = opts.gid;
+ mntinf.ttl = opts.ttl;
+ mntinf.dmode = opts.dmode;
+ mntinf.fmode = opts.fmode;
+ mntinf.dmask = opts.dmask;
+ mntinf.fmask = opts.fmask;
+
+ /*
+ * Note: When adding and/or modifying parameters of the vboxsf mounting
+ * options you also would have to adjust VBoxServiceAutoMount.cpp
+ * to keep this code here slick without having VbglR3.
+ */
+ err = mount(host_name, mount_point, "vboxsf", flags, &mntinf);
+ if (err == -1 && errno == EPROTO)
+ {
+ /* Sometimes the mount utility messes up the share name. Try to
+ * un-mangle it again. */
+ char szCWD[4096];
+ size_t cchCWD;
+ if (!getcwd(szCWD, sizeof(szCWD)))
+ panic_err("%s: failed to get the current working directory", argv[0]);
+ cchCWD = strlen(szCWD);
+ if (!strncmp(host_name, szCWD, cchCWD))
+ {
+ while (host_name[cchCWD] == '/')
+ ++cchCWD;
+ /* We checked before that we have enough space */
+ strcpy(mntinf.name, host_name + cchCWD);
+ }
+ err = mount(host_name, mount_point, "vboxsf", flags, &mntinf);
+ }
+ if (err)
+ panic_err("%s: mounting failed with the error", argv[0]);
+
+ if (!nomtab)
+ {
+ err = vbsfmount_complete(host_name, mount_point, flags, &opts);
+ switch (err)
+ {
+ case 0: /* Success. */
+ break;
+
+ case 1:
+ panic_err("%s: Could not update mount table (failed to create memstream).", argv[0]);
+ break;
+
+ case 2:
+ panic_err("%s: Could not open mount table for update.", argv[0]);
+ break;
+
+ case 3:
+ /* panic_err("%s: Could not add an entry to the mount table.", argv[0]); */
+ break;
+
+ default:
+ panic_err("%s: Unknown error while completing mount operation: %d", argv[0], err);
+ break;
+ }
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/src/VBox/Additions/linux/sharedfolders/regops.c b/src/VBox/Additions/linux/sharedfolders/regops.c
new file mode 100644
index 00000000..646d6062
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/regops.c
@@ -0,0 +1,876 @@
+/* $Id: regops.c $ */
+/** @file
+ * vboxsf - VBox Linux Shared Folders VFS, regular file inode and file operations.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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.
+ */
+
+/*
+ * Limitations: only COW memory mapping is supported
+ */
+
+#include "vfsmod.h"
+
+static void *alloc_bounce_buffer(size_t * tmp_sizep, PRTCCPHYS physp, size_t
+ xfer_size, const char *caller)
+{
+ size_t tmp_size;
+ void *tmp;
+
+ /* try for big first. */
+ tmp_size = RT_ALIGN_Z(xfer_size, PAGE_SIZE);
+ if (tmp_size > 16U * _1K)
+ tmp_size = 16U * _1K;
+ tmp = kmalloc(tmp_size, GFP_KERNEL);
+ if (!tmp) {
+ /* fall back on a page sized buffer. */
+ tmp = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!tmp) {
+ LogRel(("%s: could not allocate bounce buffer for xfer_size=%zu %s\n", caller, xfer_size));
+ return NULL;
+ }
+ tmp_size = PAGE_SIZE;
+ }
+
+ *tmp_sizep = tmp_size;
+ *physp = virt_to_phys(tmp);
+ return tmp;
+}
+
+static void free_bounce_buffer(void *tmp)
+{
+ kfree(tmp);
+}
+
+/* fops */
+static int sf_reg_read_aux(const char *caller, struct sf_glob_info *sf_g,
+ struct sf_reg_info *sf_r, void *buf,
+ uint32_t * nread, uint64_t pos)
+{
+ /** @todo bird: yes, kmap() and kmalloc() input only. Since the buffer is
+ * contiguous in physical memory (kmalloc or single page), we should
+ * use a physical address here to speed things up. */
+ int rc = VbglR0SfRead(&client_handle, &sf_g->map, sf_r->handle,
+ pos, nread, buf, false /* already locked? */ );
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfRead failed. caller=%s, rc=%Rrc\n", caller,
+ rc));
+ return -EPROTO;
+ }
+ return 0;
+}
+
+static int sf_reg_write_aux(const char *caller, struct sf_glob_info *sf_g,
+ struct sf_reg_info *sf_r, void *buf,
+ uint32_t * nwritten, uint64_t pos)
+{
+ /** @todo bird: yes, kmap() and kmalloc() input only. Since the buffer is
+ * contiguous in physical memory (kmalloc or single page), we should
+ * use a physical address here to speed things up. */
+ int rc = VbglR0SfWrite(&client_handle, &sf_g->map, sf_r->handle,
+ pos, nwritten, buf,
+ false /* already locked? */ );
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfWrite failed. caller=%s, rc=%Rrc\n",
+ caller, rc));
+ return -EPROTO;
+ }
+ return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) \
+ && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+
+void free_pipebuf(struct page *kpage)
+{
+ kunmap(kpage);
+ __free_pages(kpage, 0);
+}
+
+void *sf_pipe_buf_map(struct pipe_inode_info *pipe,
+ struct pipe_buffer *pipe_buf, int atomic)
+{
+ return 0;
+}
+
+void sf_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *pipe_buf)
+{
+}
+
+void sf_pipe_buf_unmap(struct pipe_inode_info *pipe,
+ struct pipe_buffer *pipe_buf, void *map_data)
+{
+}
+
+int sf_pipe_buf_steal(struct pipe_inode_info *pipe,
+ struct pipe_buffer *pipe_buf)
+{
+ return 0;
+}
+
+static void sf_pipe_buf_release(struct pipe_inode_info *pipe,
+ struct pipe_buffer *pipe_buf)
+{
+ free_pipebuf(pipe_buf->page);
+}
+
+int sf_pipe_buf_confirm(struct pipe_inode_info *info,
+ struct pipe_buffer *pipe_buf)
+{
+ return 0;
+}
+
+static struct pipe_buf_operations sf_pipe_buf_ops = {
+ .can_merge = 0,
+ .map = sf_pipe_buf_map,
+ .unmap = sf_pipe_buf_unmap,
+ .confirm = sf_pipe_buf_confirm,
+ .release = sf_pipe_buf_release,
+ .steal = sf_pipe_buf_steal,
+ .get = sf_pipe_buf_get,
+};
+
+#define LOCK_PIPE(pipe) \
+ if (pipe->inode) \
+ mutex_lock(&pipe->inode->i_mutex);
+
+#define UNLOCK_PIPE(pipe) \
+ if (pipe->inode) \
+ mutex_unlock(&pipe->inode->i_mutex);
+
+ssize_t
+sf_splice_read(struct file *in, loff_t * poffset,
+ struct pipe_inode_info *pipe, size_t len, unsigned int flags)
+{
+ size_t bytes_remaining = len;
+ loff_t orig_offset = *poffset;
+ loff_t offset = orig_offset;
+ struct inode *inode = GET_F_DENTRY(in)->d_inode;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_reg_info *sf_r = in->private_data;
+ ssize_t retval;
+ struct page *kpage = 0;
+ size_t nsent = 0;
+
+ TRACE();
+ if (!S_ISREG(inode->i_mode)) {
+ LogFunc(("read from non regular file %d\n", inode->i_mode));
+ return -EINVAL;
+ }
+ if (!len) {
+ return 0;
+ }
+
+ LOCK_PIPE(pipe);
+
+ uint32_t req_size = 0;
+ while (bytes_remaining > 0) {
+ kpage = alloc_page(GFP_KERNEL);
+ if (unlikely(kpage == NULL)) {
+ UNLOCK_PIPE(pipe);
+ return -ENOMEM;
+ }
+ req_size = 0;
+ uint32_t nread = req_size =
+ (uint32_t) min(bytes_remaining, (size_t) PAGE_SIZE);
+ uint32_t chunk = 0;
+ void *kbuf = kmap(kpage);
+ while (chunk < req_size) {
+ retval =
+ sf_reg_read_aux(__func__, sf_g, sf_r, kbuf + chunk,
+ &nread, offset);
+ if (retval < 0)
+ goto err;
+ if (nread == 0)
+ break;
+ chunk += nread;
+ offset += nread;
+ nread = req_size - chunk;
+ }
+ if (!pipe->readers) {
+ send_sig(SIGPIPE, current, 0);
+ retval = -EPIPE;
+ goto err;
+ }
+ if (pipe->nrbufs < PIPE_BUFFERS) {
+ struct pipe_buffer *pipebuf =
+ pipe->bufs +
+ ((pipe->curbuf + pipe->nrbufs) & (PIPE_BUFFERS -
+ 1));
+ pipebuf->page = kpage;
+ pipebuf->ops = &sf_pipe_buf_ops;
+ pipebuf->len = req_size;
+ pipebuf->offset = 0;
+ pipebuf->private = 0;
+ pipebuf->flags = 0;
+ pipe->nrbufs++;
+ nsent += req_size;
+ bytes_remaining -= req_size;
+ if (signal_pending(current))
+ break;
+ } else { /* pipe full */
+
+ if (flags & SPLICE_F_NONBLOCK) {
+ retval = -EAGAIN;
+ goto err;
+ }
+ free_pipebuf(kpage);
+ break;
+ }
+ }
+ UNLOCK_PIPE(pipe);
+ if (!nsent && signal_pending(current))
+ return -ERESTARTSYS;
+ *poffset += nsent;
+ return offset - orig_offset;
+
+ err:
+ UNLOCK_PIPE(pipe);
+ free_pipebuf(kpage);
+ return retval;
+}
+
+#endif /* 2.6.23 <= LINUX_VERSION_CODE < 2.6.31 */
+
+/**
+ * Read from a regular file.
+ *
+ * @param file the file
+ * @param buf the buffer
+ * @param size length of the buffer
+ * @param off offset within the file
+ * @returns the number of read bytes on success, Linux error code otherwise
+ */
+static ssize_t sf_reg_read(struct file *file, char *buf, size_t size,
+ loff_t * off)
+{
+ int err;
+ void *tmp;
+ RTCCPHYS tmp_phys;
+ size_t tmp_size;
+ size_t left = size;
+ ssize_t total_bytes_read = 0;
+ struct inode *inode = GET_F_DENTRY(file)->d_inode;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_reg_info *sf_r = file->private_data;
+ loff_t pos = *off;
+
+ TRACE();
+ if (!S_ISREG(inode->i_mode)) {
+ LogFunc(("read from non regular file %d\n", inode->i_mode));
+ return -EINVAL;
+ }
+
+ /** @todo XXX Check read permission according to inode->i_mode! */
+
+ if (!size)
+ return 0;
+
+ tmp =
+ alloc_bounce_buffer(&tmp_size, &tmp_phys, size,
+ __PRETTY_FUNCTION__);
+ if (!tmp)
+ return -ENOMEM;
+
+ while (left) {
+ uint32_t to_read, nread;
+
+ to_read = tmp_size;
+ if (to_read > left)
+ to_read = (uint32_t) left;
+
+ nread = to_read;
+
+ err = sf_reg_read_aux(__func__, sf_g, sf_r, tmp, &nread, pos);
+ if (err)
+ goto fail;
+
+ if (copy_to_user(buf, tmp, nread)) {
+ err = -EFAULT;
+ goto fail;
+ }
+
+ pos += nread;
+ left -= nread;
+ buf += nread;
+ total_bytes_read += nread;
+ if (nread != to_read)
+ break;
+ }
+
+ *off += total_bytes_read;
+ free_bounce_buffer(tmp);
+ return total_bytes_read;
+
+ fail:
+ free_bounce_buffer(tmp);
+ return err;
+}
+
+/**
+ * Write to a regular file.
+ *
+ * @param file the file
+ * @param buf the buffer
+ * @param size length of the buffer
+ * @param off offset within the file
+ * @returns the number of written bytes on success, Linux error code otherwise
+ */
+static ssize_t sf_reg_write(struct file *file, const char *buf, size_t size,
+ loff_t * off)
+{
+ int err;
+ void *tmp;
+ RTCCPHYS tmp_phys;
+ size_t tmp_size;
+ size_t left = size;
+ ssize_t total_bytes_written = 0;
+ struct inode *inode = GET_F_DENTRY(file)->d_inode;
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_reg_info *sf_r = file->private_data;
+ loff_t pos;
+
+ TRACE();
+ BUG_ON(!sf_i);
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_r);
+
+ if (!S_ISREG(inode->i_mode)) {
+ LogFunc(("write to non regular file %d\n", inode->i_mode));
+ return -EINVAL;
+ }
+
+ pos = *off;
+ if (file->f_flags & O_APPEND) {
+ pos = inode->i_size;
+ *off = pos;
+ }
+
+ /** @todo XXX Check write permission according to inode->i_mode! */
+
+ if (!size)
+ return 0;
+
+ tmp =
+ alloc_bounce_buffer(&tmp_size, &tmp_phys, size,
+ __PRETTY_FUNCTION__);
+ if (!tmp)
+ return -ENOMEM;
+
+ while (left) {
+ uint32_t to_write, nwritten;
+
+ to_write = tmp_size;
+ if (to_write > left)
+ to_write = (uint32_t) left;
+
+ nwritten = to_write;
+
+ if (copy_from_user(tmp, buf, to_write)) {
+ err = -EFAULT;
+ goto fail;
+ }
+
+ err =
+ VbglR0SfWritePhysCont(&client_handle, &sf_g->map,
+ sf_r->handle, pos, &nwritten,
+ tmp_phys);
+ err = RT_FAILURE(err) ? -EPROTO : 0;
+ if (err)
+ goto fail;
+
+ pos += nwritten;
+ left -= nwritten;
+ buf += nwritten;
+ total_bytes_written += nwritten;
+ if (nwritten != to_write)
+ break;
+ }
+
+ *off += total_bytes_written;
+ if (*off > inode->i_size)
+ inode->i_size = *off;
+
+ sf_i->force_restat = 1;
+ free_bounce_buffer(tmp);
+ return total_bytes_written;
+
+ fail:
+ free_bounce_buffer(tmp);
+ return err;
+}
+
+/**
+ * Open a regular file.
+ *
+ * @param inode the inode
+ * @param file the file
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_reg_open(struct inode *inode, struct file *file)
+{
+ int rc, rc_linux = 0;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+ struct sf_reg_info *sf_r;
+ SHFLCREATEPARMS params;
+
+ TRACE();
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_i);
+
+ LogFunc(("open %s\n", sf_i->path->String.utf8));
+
+ sf_r = kmalloc(sizeof(*sf_r), GFP_KERNEL);
+ if (!sf_r) {
+ LogRelFunc(("could not allocate reg info\n"));
+ return -ENOMEM;
+ }
+
+ /* Already open? */
+ if (sf_i->handle != SHFL_HANDLE_NIL) {
+ /*
+ * This inode was created with sf_create_aux(). Check the CreateFlags:
+ * O_CREAT, O_TRUNC: inherent true (file was just created). Not sure
+ * about the access flags (SHFL_CF_ACCESS_*).
+ */
+ sf_i->force_restat = 1;
+ sf_r->handle = sf_i->handle;
+ sf_i->handle = SHFL_HANDLE_NIL;
+ sf_i->file = file;
+ file->private_data = sf_r;
+ return 0;
+ }
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ /* We check the value of params.Handle afterwards to find out if
+ * the call succeeded or failed, as the API does not seem to cleanly
+ * distinguish error and informational messages.
+ *
+ * Furthermore, we must set params.Handle to SHFL_HANDLE_NIL to
+ * make the shared folders host service use our fMode parameter */
+
+ if (file->f_flags & O_CREAT) {
+ LogFunc(("O_CREAT set\n"));
+ params.CreateFlags |= SHFL_CF_ACT_CREATE_IF_NEW;
+ /* We ignore O_EXCL, as the Linux kernel seems to call create
+ beforehand itself, so O_EXCL should always fail. */
+ if (file->f_flags & O_TRUNC) {
+ LogFunc(("O_TRUNC set\n"));
+ params.CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS;
+ } else
+ params.CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
+ } else {
+ params.CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
+ if (file->f_flags & O_TRUNC) {
+ LogFunc(("O_TRUNC set\n"));
+ params.CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS;
+ }
+ }
+
+ switch (file->f_flags & O_ACCMODE) {
+ case O_RDONLY:
+ params.CreateFlags |= SHFL_CF_ACCESS_READ;
+ break;
+
+ case O_WRONLY:
+ params.CreateFlags |= SHFL_CF_ACCESS_WRITE;
+ break;
+
+ case O_RDWR:
+ params.CreateFlags |= SHFL_CF_ACCESS_READWRITE;
+ break;
+
+ default:
+ BUG();
+ }
+
+ if (file->f_flags & O_APPEND) {
+ LogFunc(("O_APPEND set\n"));
+ params.CreateFlags |= SHFL_CF_ACCESS_APPEND;
+ }
+
+ params.Info.Attr.fMode = inode->i_mode;
+ LogFunc(("sf_reg_open: calling VbglR0SfCreate, file %s, flags=%#x, %#x\n", sf_i->path->String.utf8, file->f_flags, params.CreateFlags));
+ rc = VbglR0SfCreate(&client_handle, &sf_g->map, sf_i->path, &params);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfCreate failed flags=%d,%#x rc=%Rrc\n",
+ file->f_flags, params.CreateFlags, rc));
+ kfree(sf_r);
+ return -RTErrConvertToErrno(rc);
+ }
+
+ if (SHFL_HANDLE_NIL == params.Handle) {
+ switch (params.Result) {
+ case SHFL_PATH_NOT_FOUND:
+ case SHFL_FILE_NOT_FOUND:
+ rc_linux = -ENOENT;
+ break;
+ case SHFL_FILE_EXISTS:
+ rc_linux = -EEXIST;
+ break;
+ default:
+ break;
+ }
+ }
+
+ sf_i->force_restat = 1;
+ sf_r->handle = params.Handle;
+ sf_i->file = file;
+ file->private_data = sf_r;
+ return rc_linux;
+}
+
+/**
+ * Close a regular file.
+ *
+ * @param inode the inode
+ * @param file the file
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_reg_release(struct inode *inode, struct file *file)
+{
+ int rc;
+ struct sf_reg_info *sf_r;
+ struct sf_glob_info *sf_g;
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+
+ TRACE();
+ sf_g = GET_GLOB_INFO(inode->i_sb);
+ sf_r = file->private_data;
+
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_r);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
+ /* See the smbfs source (file.c). mmap in particular can cause data to be
+ * written to the file after it is closed, which we can't cope with. We
+ * copy and paste the body of filemap_write_and_wait() here as it was not
+ * defined before 2.6.6 and not exported until quite a bit later. */
+ /* filemap_write_and_wait(inode->i_mapping); */
+ if (inode->i_mapping->nrpages
+ && filemap_fdatawrite(inode->i_mapping) != -EIO)
+ filemap_fdatawait(inode->i_mapping);
+#endif
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, sf_r->handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("VbglR0SfClose failed rc=%Rrc\n", rc));
+
+ kfree(sf_r);
+ sf_i->file = NULL;
+ sf_i->handle = SHFL_HANDLE_NIL;
+ file->private_data = NULL;
+ return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+static int sf_reg_fault(struct vm_fault *vmf)
+#elif LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+static int sf_reg_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+static struct page *sf_reg_nopage(struct vm_area_struct *vma,
+ unsigned long vaddr, int *type)
+# define SET_TYPE(t) *type = (t)
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) */
+static struct page *sf_reg_nopage(struct vm_area_struct *vma,
+ unsigned long vaddr, int unused)
+# define SET_TYPE(t)
+#endif
+{
+ struct page *page;
+ char *buf;
+ loff_t off;
+ uint32_t nread = PAGE_SIZE;
+ int err;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+ struct vm_area_struct *vma = vmf->vma;
+#endif
+ struct file *file = vma->vm_file;
+ struct inode *inode = GET_F_DENTRY(file)->d_inode;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_reg_info *sf_r = file->private_data;
+
+ TRACE();
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ if (vmf->pgoff > vma->vm_end)
+ return VM_FAULT_SIGBUS;
+#else
+ if (vaddr > vma->vm_end) {
+ SET_TYPE(VM_FAULT_SIGBUS);
+ return NOPAGE_SIGBUS;
+ }
+#endif
+
+ /* Don't use GFP_HIGHUSER as long as sf_reg_read_aux() calls VbglR0SfRead()
+ * which works on virtual addresses. On Linux cannot reliably determine the
+ * physical address for high memory, see rtR0MemObjNativeLockKernel(). */
+ page = alloc_page(GFP_USER);
+ if (!page) {
+ LogRelFunc(("failed to allocate page\n"));
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ return VM_FAULT_OOM;
+#else
+ SET_TYPE(VM_FAULT_OOM);
+ return NOPAGE_OOM;
+#endif
+ }
+
+ buf = kmap(page);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ off = (vmf->pgoff << PAGE_SHIFT);
+#else
+ off = (vaddr - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
+#endif
+ err = sf_reg_read_aux(__func__, sf_g, sf_r, buf, &nread, off);
+ if (err) {
+ kunmap(page);
+ put_page(page);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ return VM_FAULT_SIGBUS;
+#else
+ SET_TYPE(VM_FAULT_SIGBUS);
+ return NOPAGE_SIGBUS;
+#endif
+ }
+
+ BUG_ON(nread > PAGE_SIZE);
+ if (!nread) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ clear_user_page(page_address(page), vmf->pgoff, page);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ clear_user_page(page_address(page), vaddr, page);
+#else
+ clear_user_page(page_address(page), vaddr);
+#endif
+ } else
+ memset(buf + nread, 0, PAGE_SIZE - nread);
+
+ flush_dcache_page(page);
+ kunmap(page);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ vmf->page = page;
+ return 0;
+#else
+ SET_TYPE(VM_FAULT_MAJOR);
+ return page;
+#endif
+}
+
+static struct vm_operations_struct sf_vma_ops = {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ .fault = sf_reg_fault
+#else
+ .nopage = sf_reg_nopage
+#endif
+};
+
+static int sf_reg_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ TRACE();
+ if (vma->vm_flags & VM_SHARED) {
+ LogFunc(("shared mmapping not available\n"));
+ return -EINVAL;
+ }
+
+ vma->vm_ops = &sf_vma_ops;
+ return 0;
+}
+
+struct file_operations sf_reg_fops = {
+ .read = sf_reg_read,
+ .open = sf_reg_open,
+ .write = sf_reg_write,
+ .release = sf_reg_release,
+ .mmap = sf_reg_mmap,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+/** @todo This code is known to cause caching of data which should not be
+ * cached. Investigate. */
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+ .splice_read = sf_splice_read,
+# else
+ .sendfile = generic_file_sendfile,
+# endif
+ .aio_read = generic_file_aio_read,
+ .aio_write = generic_file_aio_write,
+# endif
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+ .fsync = noop_fsync,
+# else
+ .fsync = simple_sync_file,
+# endif
+ .llseek = generic_file_llseek,
+#endif
+};
+
+struct inode_operations sf_reg_iops = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+ .revalidate = sf_inode_revalidate
+#else
+ .getattr = sf_getattr,
+ .setattr = sf_setattr
+#endif
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+
+static int sf_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = GET_F_DENTRY(file)->d_inode;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_reg_info *sf_r = file->private_data;
+ uint32_t nread = PAGE_SIZE;
+ char *buf;
+ loff_t off = ((loff_t) page->index) << PAGE_SHIFT;
+ int ret;
+
+ TRACE();
+
+ buf = kmap(page);
+ ret = sf_reg_read_aux(__func__, sf_g, sf_r, buf, &nread, off);
+ if (ret) {
+ kunmap(page);
+ if (PageLocked(page))
+ unlock_page(page);
+ return ret;
+ }
+ BUG_ON(nread > PAGE_SIZE);
+ memset(&buf[nread], 0, PAGE_SIZE - nread);
+ flush_dcache_page(page);
+ kunmap(page);
+ SetPageUptodate(page);
+ unlock_page(page);
+ return 0;
+}
+
+static int sf_writepage(struct page *page, struct writeback_control *wbc)
+{
+ struct address_space *mapping = page->mapping;
+ struct inode *inode = mapping->host;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+ struct file *file = sf_i->file;
+ struct sf_reg_info *sf_r = file->private_data;
+ char *buf;
+ uint32_t nwritten = PAGE_SIZE;
+ int end_index = inode->i_size >> PAGE_SHIFT;
+ loff_t off = ((loff_t) page->index) << PAGE_SHIFT;
+ int err;
+
+ TRACE();
+
+ if (page->index >= end_index)
+ nwritten = inode->i_size & (PAGE_SIZE - 1);
+
+ buf = kmap(page);
+
+ err = sf_reg_write_aux(__func__, sf_g, sf_r, buf, &nwritten, off);
+ if (err < 0) {
+ ClearPageUptodate(page);
+ goto out;
+ }
+
+ if (off > inode->i_size)
+ inode->i_size = off;
+
+ if (PageError(page))
+ ClearPageError(page);
+ err = 0;
+
+ out:
+ kunmap(page);
+
+ unlock_page(page);
+ return err;
+}
+
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+
+int sf_write_begin(struct file *file, struct address_space *mapping, loff_t pos,
+ unsigned len, unsigned flags, struct page **pagep,
+ void **fsdata)
+{
+ TRACE();
+
+ return simple_write_begin(file, mapping, pos, len, flags, pagep,
+ fsdata);
+}
+
+int sf_write_end(struct file *file, struct address_space *mapping, loff_t pos,
+ unsigned len, unsigned copied, struct page *page, void *fsdata)
+{
+ struct inode *inode = mapping->host;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_reg_info *sf_r = file->private_data;
+ void *buf;
+ unsigned from = pos & (PAGE_SIZE - 1);
+ uint32_t nwritten = len;
+ int err;
+
+ TRACE();
+
+ buf = kmap(page);
+ err =
+ sf_reg_write_aux(__func__, sf_g, sf_r, buf + from, &nwritten, pos);
+ kunmap(page);
+
+ if (err >= 0) {
+ if (!PageUptodate(page) && nwritten == PAGE_SIZE)
+ SetPageUptodate(page);
+
+ pos += nwritten;
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+ }
+
+ unlock_page(page);
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
+ put_page(page);
+# else
+ page_cache_release(page);
+# endif
+
+ return nwritten;
+}
+
+# endif /* KERNEL_VERSION >= 2.6.24 */
+
+struct address_space_operations sf_reg_aops = {
+ .readpage = sf_readpage,
+ .writepage = sf_writepage,
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+ .write_begin = sf_write_begin,
+ .write_end = sf_write_end,
+# else
+ .prepare_write = simple_prepare_write,
+ .commit_write = simple_commit_write,
+# endif
+};
+
+#endif /* LINUX_VERSION_CODE >= 2.6.0 */
+
diff --git a/src/VBox/Additions/linux/sharedfolders/utils.c b/src/VBox/Additions/linux/sharedfolders/utils.c
new file mode 100644
index 00000000..d14bbb31
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/utils.c
@@ -0,0 +1,883 @@
+/* $Id: utils.c $ */
+/** @file
+ * vboxsf - VBox Linux Shared Folders VFS, utility functions.
+ *
+ * Utility functions (mainly conversion from/to VirtualBox/Linux data structures).
+ */
+
+/*
+ * Copyright (C) 2006-2019 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.
+ */
+
+#include "vfsmod.h"
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <linux/nfs_fs.h>
+#include <linux/vfs.h>
+
+/* #define USE_VMALLOC */
+
+/*
+ * sf_reg_aops and sf_backing_dev_info are just quick implementations to make
+ * sendfile work. For more information have a look at
+ *
+ * http://us1.samba.org/samba/ftp/cifs-cvs/ols2006-fs-tutorial-smf.odp
+ *
+ * and the sample implementation
+ *
+ * http://pserver.samba.org/samba/ftp/cifs-cvs/samplefs.tar.gz
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+static void sf_ftime_from_timespec(time_t * time, RTTIMESPEC * ts)
+{
+ int64_t t = RTTimeSpecGetNano(ts);
+
+ do_div(t, 1000000000);
+ *time = t;
+}
+
+static void sf_timespec_from_ftime(RTTIMESPEC * ts, time_t * time)
+{
+ int64_t t = 1000000000 * *time;
+ RTTimeSpecSetNano(ts, t);
+}
+#else /* >= 2.6.0 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
+static void sf_ftime_from_timespec(struct timespec *tv, RTTIMESPEC *ts)
+#else
+static void sf_ftime_from_timespec(struct timespec64 *tv, RTTIMESPEC *ts)
+#endif
+{
+ int64_t t = RTTimeSpecGetNano(ts);
+ int64_t nsec;
+
+ nsec = do_div(t, 1000000000);
+ tv->tv_sec = t;
+ tv->tv_nsec = nsec;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
+static void sf_timespec_from_ftime(RTTIMESPEC *ts, struct timespec *tv)
+#else
+static void sf_timespec_from_ftime(RTTIMESPEC *ts, struct timespec64 *tv)
+#endif
+{
+ int64_t t = (int64_t) tv->tv_nsec + (int64_t) tv->tv_sec * 1000000000;
+ RTTimeSpecSetNano(ts, t);
+}
+#endif /* >= 2.6.0 */
+
+/* set [inode] attributes based on [info], uid/gid based on [sf_g] */
+void sf_init_inode(struct sf_glob_info *sf_g, struct inode *inode,
+ PSHFLFSOBJINFO info)
+{
+ PSHFLFSOBJATTR attr;
+ int mode;
+
+ TRACE();
+
+ attr = &info->Attr;
+
+#define mode_set(r) attr->fMode & (RTFS_UNIX_##r) ? (S_##r) : 0;
+ mode = mode_set(IRUSR);
+ mode |= mode_set(IWUSR);
+ mode |= mode_set(IXUSR);
+
+ mode |= mode_set(IRGRP);
+ mode |= mode_set(IWGRP);
+ mode |= mode_set(IXGRP);
+
+ mode |= mode_set(IROTH);
+ mode |= mode_set(IWOTH);
+ mode |= mode_set(IXOTH);
+
+#undef mode_set
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ inode->i_mapping->a_ops = &sf_reg_aops;
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 19, 0)
+ /* XXX Was this ever necessary? */
+ inode->i_mapping->backing_dev_info = &sf_g->bdi;
+#endif
+#endif
+
+ if (RTFS_IS_DIRECTORY(attr->fMode)) {
+ inode->i_mode = sf_g->dmode != ~0 ? (sf_g->dmode & 0777) : mode;
+ inode->i_mode &= ~sf_g->dmask;
+ inode->i_mode |= S_IFDIR;
+ inode->i_op = &sf_dir_iops;
+ inode->i_fop = &sf_dir_fops;
+ /* XXX: this probably should be set to the number of entries
+ in the directory plus two (. ..) */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+ set_nlink(inode, 1);
+#else
+ inode->i_nlink = 1;
+#endif
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ else if (RTFS_IS_SYMLINK(attr->fMode)) {
+ inode->i_mode = sf_g->fmode != ~0 ? (sf_g->fmode & 0777) : mode;
+ inode->i_mode &= ~sf_g->fmask;
+ inode->i_mode |= S_IFLNK;
+ inode->i_op = &sf_lnk_iops;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+ set_nlink(inode, 1);
+#else
+ inode->i_nlink = 1;
+#endif
+ }
+#endif
+ else {
+ inode->i_mode = sf_g->fmode != ~0 ? (sf_g->fmode & 0777) : mode;
+ inode->i_mode &= ~sf_g->fmask;
+ inode->i_mode |= S_IFREG;
+ inode->i_op = &sf_reg_iops;
+ inode->i_fop = &sf_reg_fops;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+ set_nlink(inode, 1);
+#else
+ inode->i_nlink = 1;
+#endif
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+ inode->i_uid = make_kuid(current_user_ns(), sf_g->uid);
+ inode->i_gid = make_kgid(current_user_ns(), sf_g->gid);
+#else
+ inode->i_uid = sf_g->uid;
+ inode->i_gid = sf_g->gid;
+#endif
+
+ inode->i_size = info->cbObject;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) && !defined(KERNEL_FC6)
+ inode->i_blksize = 4096;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 11)
+ inode->i_blkbits = 12;
+#endif
+ /* i_blocks always in units of 512 bytes! */
+ inode->i_blocks = (info->cbAllocated + 511) / 512;
+
+ sf_ftime_from_timespec(&inode->i_atime, &info->AccessTime);
+ sf_ftime_from_timespec(&inode->i_ctime, &info->ChangeTime);
+ sf_ftime_from_timespec(&inode->i_mtime, &info->ModificationTime);
+}
+
+int sf_stat(const char *caller, struct sf_glob_info *sf_g,
+ SHFLSTRING * path, PSHFLFSOBJINFO result, int ok_to_fail)
+{
+ int rc;
+ SHFLCREATEPARMS params;
+ NOREF(caller);
+
+ TRACE();
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ params.CreateFlags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;
+ LogFunc(("sf_stat: calling VbglR0SfCreate, file %s, flags %#x\n",
+ path->String.utf8, params.CreateFlags));
+ rc = VbglR0SfCreate(&client_handle, &sf_g->map, path, &params);
+ if (rc == VERR_INVALID_NAME) {
+ /* this can happen for names like 'foo*' on a Windows host */
+ return -ENOENT;
+ }
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfCreate(%s) failed. caller=%s, rc=%Rrc\n",
+ path->String.utf8, rc, caller));
+ return -EPROTO;
+ }
+ if (params.Result != SHFL_FILE_EXISTS) {
+ if (!ok_to_fail)
+ LogFunc(("VbglR0SfCreate(%s) file does not exist. caller=%s, result=%d\n", path->String.utf8, params.Result, caller));
+ return -ENOENT;
+ }
+
+ *result = params.Info;
+ return 0;
+}
+
+/* this is called directly as iop on 2.4, indirectly as dop
+ [sf_dentry_revalidate] on 2.4/2.6, indirectly as iop through
+ [sf_getattr] on 2.6. the job is to find out whether dentry/inode is
+ still valid. the test is failed if [dentry] does not have an inode
+ or [sf_stat] is unsuccessful, otherwise we return success and
+ update inode attributes */
+int sf_inode_revalidate(struct dentry *dentry)
+{
+ int err;
+ struct sf_glob_info *sf_g;
+ struct sf_inode_info *sf_i;
+ SHFLFSOBJINFO info;
+
+ TRACE();
+ if (!dentry || !dentry->d_inode) {
+ LogFunc(("no dentry(%p) or inode(%p)\n", dentry,
+ dentry->d_inode));
+ return -EINVAL;
+ }
+
+ sf_g = GET_GLOB_INFO(dentry->d_inode->i_sb);
+ sf_i = GET_INODE_INFO(dentry->d_inode);
+
+#if 0
+ printk("%s called by %p:%p\n",
+ sf_i->path->String.utf8,
+ __builtin_return_address(0), __builtin_return_address(1));
+#endif
+
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_i);
+
+ if (!sf_i->force_restat) {
+ if (jiffies - dentry->d_time < sf_g->ttl)
+ return 0;
+ }
+
+ err = sf_stat(__func__, sf_g, sf_i->path, &info, 1);
+ if (err)
+ return err;
+
+ dentry->d_time = jiffies;
+ sf_init_inode(sf_g, dentry->d_inode, &info);
+ return 0;
+}
+
+/* this is called during name resolution/lookup to check if the
+ [dentry] in the cache is still valid. the job is handled by
+ [sf_inode_revalidate] */
+static int
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+sf_dentry_revalidate(struct dentry *dentry, unsigned flags)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+sf_dentry_revalidate(struct dentry *dentry, struct nameidata *nd)
+#else
+sf_dentry_revalidate(struct dentry *dentry, int flags)
+#endif
+{
+ TRACE();
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ if (flags & LOOKUP_RCU)
+ return -ECHILD;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
+ /* see Documentation/filesystems/vfs.txt */
+ if (nd && nd->flags & LOOKUP_RCU)
+ return -ECHILD;
+#endif
+
+ if (sf_inode_revalidate(dentry))
+ return 0;
+
+ return 1;
+}
+
+/* on 2.6 this is a proxy for [sf_inode_revalidate] which (as a side
+ effect) updates inode attributes for [dentry] (given that [dentry]
+ has inode at all) from these new attributes we derive [kstat] via
+ [generic_fillattr] */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+int sf_getattr(const struct path *path, struct kstat *kstat, u32 request_mask,
+ unsigned int flags)
+#else
+int sf_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *kstat)
+#endif
+{
+ int err;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+ struct dentry *dentry = path->dentry;
+#endif
+
+ TRACE();
+ err = sf_inode_revalidate(dentry);
+ if (err)
+ return err;
+
+ generic_fillattr(dentry->d_inode, kstat);
+ return 0;
+}
+
+int sf_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ struct sf_glob_info *sf_g;
+ struct sf_inode_info *sf_i;
+ SHFLCREATEPARMS params;
+ SHFLFSOBJINFO info;
+ uint32_t cbBuffer;
+ int rc, err;
+
+ TRACE();
+
+ sf_g = GET_GLOB_INFO(dentry->d_inode->i_sb);
+ sf_i = GET_INODE_INFO(dentry->d_inode);
+ err = 0;
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ params.CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS
+ | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_ATTR_WRITE;
+
+ /* this is at least required for Posix hosts */
+ if (iattr->ia_valid & ATTR_SIZE)
+ params.CreateFlags |= SHFL_CF_ACCESS_WRITE;
+
+ rc = VbglR0SfCreate(&client_handle, &sf_g->map, sf_i->path, &params);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfCreate(%s) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+ err = -RTErrConvertToErrno(rc);
+ goto fail2;
+ }
+ if (params.Result != SHFL_FILE_EXISTS) {
+ LogFunc(("file %s does not exist\n", sf_i->path->String.utf8));
+ err = -ENOENT;
+ goto fail1;
+ }
+
+ /* Setting the file size and setting the other attributes has to be
+ * handled separately, see implementation of vbsfSetFSInfo() in
+ * vbsf.cpp */
+ if (iattr->ia_valid & (ATTR_MODE | ATTR_ATIME | ATTR_MTIME)) {
+#define mode_set(r) ((iattr->ia_mode & (S_##r)) ? RTFS_UNIX_##r : 0)
+
+ RT_ZERO(info);
+ if (iattr->ia_valid & ATTR_MODE) {
+ info.Attr.fMode = mode_set(IRUSR);
+ info.Attr.fMode |= mode_set(IWUSR);
+ info.Attr.fMode |= mode_set(IXUSR);
+ info.Attr.fMode |= mode_set(IRGRP);
+ info.Attr.fMode |= mode_set(IWGRP);
+ info.Attr.fMode |= mode_set(IXGRP);
+ info.Attr.fMode |= mode_set(IROTH);
+ info.Attr.fMode |= mode_set(IWOTH);
+ info.Attr.fMode |= mode_set(IXOTH);
+
+ if (iattr->ia_mode & S_IFDIR)
+ info.Attr.fMode |= RTFS_TYPE_DIRECTORY;
+ else
+ info.Attr.fMode |= RTFS_TYPE_FILE;
+ }
+
+ if (iattr->ia_valid & ATTR_ATIME)
+ sf_timespec_from_ftime(&info.AccessTime,
+ &iattr->ia_atime);
+ if (iattr->ia_valid & ATTR_MTIME)
+ sf_timespec_from_ftime(&info.ModificationTime,
+ &iattr->ia_mtime);
+ /* ignore ctime (inode change time) as it can't be set from userland anyway */
+
+ cbBuffer = sizeof(info);
+ rc = VbglR0SfFsInfo(&client_handle, &sf_g->map, params.Handle,
+ SHFL_INFO_SET | SHFL_INFO_FILE, &cbBuffer,
+ (PSHFLDIRINFO) & info);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfFsInfo(%s, FILE) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+ err = -RTErrConvertToErrno(rc);
+ goto fail1;
+ }
+ }
+
+ if (iattr->ia_valid & ATTR_SIZE) {
+ RT_ZERO(info);
+ info.cbObject = iattr->ia_size;
+ cbBuffer = sizeof(info);
+ rc = VbglR0SfFsInfo(&client_handle, &sf_g->map, params.Handle,
+ SHFL_INFO_SET | SHFL_INFO_SIZE, &cbBuffer,
+ (PSHFLDIRINFO) & info);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfFsInfo(%s, SIZE) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+ err = -RTErrConvertToErrno(rc);
+ goto fail1;
+ }
+ }
+
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("VbglR0SfClose(%s) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+
+ return sf_inode_revalidate(dentry);
+
+ fail1:
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("VbglR0SfClose(%s) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+
+ fail2:
+ return err;
+}
+#endif /* >= 2.6.0 */
+
+static int sf_make_path(const char *caller, struct sf_inode_info *sf_i,
+ const char *d_name, size_t d_len, SHFLSTRING ** result)
+{
+ size_t path_len, shflstring_len;
+ SHFLSTRING *tmp;
+ uint16_t p_len;
+ uint8_t *p_name;
+ int fRoot = 0;
+
+ TRACE();
+ p_len = sf_i->path->u16Length;
+ p_name = sf_i->path->String.utf8;
+
+ if (p_len == 1 && *p_name == '/') {
+ path_len = d_len + 1;
+ fRoot = 1;
+ } else {
+ /* lengths of constituents plus terminating zero plus slash */
+ path_len = p_len + d_len + 2;
+ if (path_len > 0xffff) {
+ LogFunc(("path too long. caller=%s, path_len=%zu\n",
+ caller, path_len));
+ return -ENAMETOOLONG;
+ }
+ }
+
+ shflstring_len = offsetof(SHFLSTRING, String.utf8) + path_len;
+ tmp = kmalloc(shflstring_len, GFP_KERNEL);
+ if (!tmp) {
+ LogRelFunc(("kmalloc failed, caller=%s\n", caller));
+ return -ENOMEM;
+ }
+ tmp->u16Length = path_len - 1;
+ tmp->u16Size = path_len;
+
+ if (fRoot)
+ memcpy(&tmp->String.utf8[0], d_name, d_len + 1);
+ else {
+ memcpy(&tmp->String.utf8[0], p_name, p_len);
+ tmp->String.utf8[p_len] = '/';
+ memcpy(&tmp->String.utf8[p_len + 1], d_name, d_len);
+ tmp->String.utf8[p_len + 1 + d_len] = '\0';
+ }
+
+ *result = tmp;
+ return 0;
+}
+
+/**
+ * [dentry] contains string encoded in coding system that corresponds
+ * to [sf_g]->nls, we must convert it to UTF8 here and pass down to
+ * [sf_make_path] which will allocate SHFLSTRING and fill it in
+ */
+int sf_path_from_dentry(const char *caller, struct sf_glob_info *sf_g,
+ struct sf_inode_info *sf_i, struct dentry *dentry,
+ SHFLSTRING ** result)
+{
+ int err;
+ const char *d_name;
+ size_t d_len;
+ const char *name;
+ size_t len = 0;
+
+ TRACE();
+ d_name = dentry->d_name.name;
+ d_len = dentry->d_name.len;
+
+ if (sf_g->nls) {
+ size_t in_len, i, out_bound_len;
+ const char *in;
+ char *out;
+
+ in = d_name;
+ in_len = d_len;
+
+ out_bound_len = PATH_MAX;
+ out = kmalloc(out_bound_len, GFP_KERNEL);
+ name = out;
+
+ for (i = 0; i < d_len; ++i) {
+ /* We renamed the linux kernel wchar_t type to linux_wchar_t in
+ the-linux-kernel.h, as it conflicts with the C++ type of that name. */
+ linux_wchar_t uni;
+ int nb;
+
+ nb = sf_g->nls->char2uni(in, in_len, &uni);
+ if (nb < 0) {
+ LogFunc(("nls->char2uni failed %x %d\n",
+ *in, in_len));
+ err = -EINVAL;
+ goto fail1;
+ }
+ in_len -= nb;
+ in += nb;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
+ nb = utf32_to_utf8(uni, out, out_bound_len);
+#else
+ nb = utf8_wctomb(out, uni, out_bound_len);
+#endif
+ if (nb < 0) {
+ LogFunc(("nls->uni2char failed %x %d\n",
+ uni, out_bound_len));
+ err = -EINVAL;
+ goto fail1;
+ }
+ out_bound_len -= nb;
+ out += nb;
+ len += nb;
+ }
+ if (len >= PATH_MAX - 1) {
+ err = -ENAMETOOLONG;
+ goto fail1;
+ }
+
+ LogFunc(("result(%d) = %.*s\n", len, len, name));
+ *out = 0;
+ } else {
+ name = d_name;
+ len = d_len;
+ }
+
+ err = sf_make_path(caller, sf_i, name, len, result);
+ if (name != d_name)
+ kfree(name);
+
+ return err;
+
+ fail1:
+ kfree(name);
+ return err;
+}
+
+int sf_nlscpy(struct sf_glob_info *sf_g,
+ char *name, size_t name_bound_len,
+ const unsigned char *utf8_name, size_t utf8_len)
+{
+ if (sf_g->nls) {
+ const char *in;
+ char *out;
+ size_t out_len;
+ size_t out_bound_len;
+ size_t in_bound_len;
+
+ in = utf8_name;
+ in_bound_len = utf8_len;
+
+ out = name;
+ out_len = 0;
+ out_bound_len = name_bound_len;
+
+ while (in_bound_len) {
+ int nb;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
+ unicode_t uni;
+
+ nb = utf8_to_utf32(in, in_bound_len, &uni);
+#else
+ linux_wchar_t uni;
+
+ nb = utf8_mbtowc(&uni, in, in_bound_len);
+#endif
+ if (nb < 0) {
+ LogFunc(("utf8_mbtowc failed(%s) %x:%d\n",
+ (const char *)utf8_name, *in,
+ in_bound_len));
+ return -EINVAL;
+ }
+ in += nb;
+ in_bound_len -= nb;
+
+ nb = sf_g->nls->uni2char(uni, out, out_bound_len);
+ if (nb < 0) {
+ LogFunc(("nls->uni2char failed(%s) %x:%d\n",
+ utf8_name, uni, out_bound_len));
+ return nb;
+ }
+ out += nb;
+ out_bound_len -= nb;
+ out_len += nb;
+ }
+
+ *out = 0;
+ } else {
+ if (utf8_len + 1 > name_bound_len)
+ return -ENAMETOOLONG;
+
+ memcpy(name, utf8_name, utf8_len + 1);
+ }
+ return 0;
+}
+
+static struct sf_dir_buf *sf_dir_buf_alloc(void)
+{
+ struct sf_dir_buf *b;
+
+ TRACE();
+ b = kmalloc(sizeof(*b), GFP_KERNEL);
+ if (!b) {
+ LogRelFunc(("could not alloc directory buffer\n"));
+ return NULL;
+ }
+#ifdef USE_VMALLOC
+ b->buf = vmalloc(DIR_BUFFER_SIZE);
+#else
+ b->buf = kmalloc(DIR_BUFFER_SIZE, GFP_KERNEL);
+#endif
+ if (!b->buf) {
+ kfree(b);
+ LogRelFunc(("could not alloc directory buffer storage\n"));
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&b->head);
+ b->cEntries = 0;
+ b->cbUsed = 0;
+ b->cbFree = DIR_BUFFER_SIZE;
+ return b;
+}
+
+static void sf_dir_buf_free(struct sf_dir_buf *b)
+{
+ BUG_ON(!b || !b->buf);
+
+ TRACE();
+ list_del(&b->head);
+#ifdef USE_VMALLOC
+ vfree(b->buf);
+#else
+ kfree(b->buf);
+#endif
+ kfree(b);
+}
+
+/**
+ * Free the directory buffer.
+ */
+void sf_dir_info_free(struct sf_dir_info *p)
+{
+ struct list_head *list, *pos, *tmp;
+
+ TRACE();
+ list = &p->info_list;
+ list_for_each_safe(pos, tmp, list) {
+ struct sf_dir_buf *b;
+
+ b = list_entry(pos, struct sf_dir_buf, head);
+ sf_dir_buf_free(b);
+ }
+ kfree(p);
+}
+
+/**
+ * Empty (but not free) the directory buffer.
+ */
+void sf_dir_info_empty(struct sf_dir_info *p)
+{
+ struct list_head *list, *pos, *tmp;
+ TRACE();
+ list = &p->info_list;
+ list_for_each_safe(pos, tmp, list) {
+ struct sf_dir_buf *b;
+ b = list_entry(pos, struct sf_dir_buf, head);
+ b->cEntries = 0;
+ b->cbUsed = 0;
+ b->cbFree = DIR_BUFFER_SIZE;
+ }
+}
+
+/**
+ * Create a new directory buffer descriptor.
+ */
+struct sf_dir_info *sf_dir_info_alloc(void)
+{
+ struct sf_dir_info *p;
+
+ TRACE();
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ LogRelFunc(("could not alloc directory info\n"));
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&p->info_list);
+ return p;
+}
+
+/**
+ * Search for an empty directory content buffer.
+ */
+static struct sf_dir_buf *sf_get_empty_dir_buf(struct sf_dir_info *sf_d)
+{
+ struct list_head *list, *pos;
+
+ list = &sf_d->info_list;
+ list_for_each(pos, list) {
+ struct sf_dir_buf *b;
+
+ b = list_entry(pos, struct sf_dir_buf, head);
+ if (!b)
+ return NULL;
+ else {
+ if (b->cbUsed == 0)
+ return b;
+ }
+ }
+
+ return NULL;
+}
+
+int sf_dir_read_all(struct sf_glob_info *sf_g, struct sf_inode_info *sf_i,
+ struct sf_dir_info *sf_d, SHFLHANDLE handle)
+{
+ int err;
+ SHFLSTRING *mask;
+ struct sf_dir_buf *b;
+
+ TRACE();
+ err = sf_make_path(__func__, sf_i, "*", 1, &mask);
+ if (err)
+ goto fail0;
+
+ for (;;) {
+ int rc;
+ void *buf;
+ uint32_t cbSize;
+ uint32_t cEntries;
+
+ b = sf_get_empty_dir_buf(sf_d);
+ if (!b) {
+ b = sf_dir_buf_alloc();
+ if (!b) {
+ err = -ENOMEM;
+ LogRelFunc(("could not alloc directory buffer\n"));
+ goto fail1;
+ }
+ list_add(&b->head, &sf_d->info_list);
+ }
+
+ buf = b->buf;
+ cbSize = b->cbFree;
+
+ rc = VbglR0SfDirInfo(&client_handle, &sf_g->map, handle, mask,
+ 0, 0, &cbSize, buf, &cEntries);
+ switch (rc) {
+ case VINF_SUCCESS:
+ RT_FALL_THRU();
+ case VERR_NO_MORE_FILES:
+ break;
+ case VERR_NO_TRANSLATION:
+ LogFunc(("host could not translate entry\n"));
+ /* XXX */
+ break;
+ default:
+ err = -RTErrConvertToErrno(rc);
+ LogFunc(("VbglR0SfDirInfo failed rc=%Rrc\n", rc));
+ goto fail1;
+ }
+
+ b->cEntries += cEntries;
+ b->cbFree -= cbSize;
+ b->cbUsed += cbSize;
+
+ if (RT_FAILURE(rc))
+ break;
+ }
+ err = 0;
+
+ fail1:
+ kfree(mask);
+
+ fail0:
+ return err;
+}
+
+int sf_get_volume_info(struct super_block *sb, STRUCT_STATFS * stat)
+{
+ struct sf_glob_info *sf_g;
+ SHFLVOLINFO SHFLVolumeInfo;
+ uint32_t cbBuffer;
+ int rc;
+
+ sf_g = GET_GLOB_INFO(sb);
+ cbBuffer = sizeof(SHFLVolumeInfo);
+ rc = VbglR0SfFsInfo(&client_handle, &sf_g->map, 0,
+ SHFL_INFO_GET | SHFL_INFO_VOLUME, &cbBuffer,
+ (PSHFLDIRINFO) & SHFLVolumeInfo);
+ if (RT_FAILURE(rc))
+ return -RTErrConvertToErrno(rc);
+
+ stat->f_type = NFS_SUPER_MAGIC; /* XXX vboxsf type? */
+ stat->f_bsize = SHFLVolumeInfo.ulBytesPerAllocationUnit;
+ stat->f_blocks = SHFLVolumeInfo.ullTotalAllocationBytes
+ / SHFLVolumeInfo.ulBytesPerAllocationUnit;
+ stat->f_bfree = SHFLVolumeInfo.ullAvailableAllocationBytes
+ / SHFLVolumeInfo.ulBytesPerAllocationUnit;
+ stat->f_bavail = SHFLVolumeInfo.ullAvailableAllocationBytes
+ / SHFLVolumeInfo.ulBytesPerAllocationUnit;
+ stat->f_files = 1000;
+ stat->f_ffree = 1000; /* don't return 0 here since the guest may think
+ * that it is not possible to create any more files */
+ stat->f_fsid.val[0] = 0;
+ stat->f_fsid.val[1] = 0;
+ stat->f_namelen = 255;
+ return 0;
+}
+
+struct dentry_operations sf_dentry_ops = {
+ .d_revalidate = sf_dentry_revalidate
+};
+
+int sf_init_backing_dev(struct sf_glob_info *sf_g)
+{
+ int rc = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && LINUX_VERSION_CODE <= KERNEL_VERSION(3, 19, 0)
+ /* Each new shared folder map gets a new uint64_t identifier,
+ * allocated in sequence. We ASSUME the sequence will not wrap. */
+ static uint64_t s_u64Sequence = 0;
+ uint64_t u64CurrentSequence = ASMAtomicIncU64(&s_u64Sequence);
+
+ sf_g->bdi.ra_pages = 0; /* No readahead */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)
+ sf_g->bdi.capabilities = BDI_CAP_MAP_DIRECT /* MAP_SHARED */
+ | BDI_CAP_MAP_COPY /* MAP_PRIVATE */
+ | BDI_CAP_READ_MAP /* can be mapped for reading */
+ | BDI_CAP_WRITE_MAP /* can be mapped for writing */
+ | BDI_CAP_EXEC_MAP; /* can be mapped for execution */
+#endif /* >= 2.6.12 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+ rc = bdi_init(&sf_g->bdi);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+ if (!rc)
+ rc = bdi_register(&sf_g->bdi, NULL, "vboxsf-%llu",
+ (unsigned long long)u64CurrentSequence);
+#endif /* >= 2.6.26 */
+#endif /* >= 2.6.24 */
+#endif /* >= 2.6.0 && <= 3.19.0 */
+ return rc;
+}
+
+void sf_done_backing_dev(struct sf_glob_info *sf_g)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) && LINUX_VERSION_CODE <= KERNEL_VERSION(3, 19, 0)
+ bdi_destroy(&sf_g->bdi); /* includes bdi_unregister() */
+#endif
+}
diff --git a/src/VBox/Additions/linux/sharedfolders/vbsfmount.c b/src/VBox/Additions/linux/sharedfolders/vbsfmount.c
new file mode 100644
index 00000000..5d68e931
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/vbsfmount.c
@@ -0,0 +1,95 @@
+/* $Id: vbsfmount.c $ */
+/** @file
+ * vbsfmount - Commonly used code to mount shared folders on Linux-based
+ * systems. Currently used by mount.vboxsf and VBoxService.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include "vbsfmount.h"
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <ctype.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+
+
+/** @todo Use defines for return values! */
+int vbsfmount_complete(const char *host_name, const char *mount_point,
+ unsigned long flags, struct vbsf_mount_opts *opts)
+{
+ FILE *f, *m;
+ char *buf;
+ size_t size;
+ struct mntent e;
+ int rc = 0;
+
+ m = open_memstream(&buf, &size);
+ if (!m)
+ return 1; /* Could not update mount table (failed to create memstream). */
+
+ if (opts->uid)
+ fprintf(m, "uid=%d,", opts->uid);
+ if (opts->gid)
+ fprintf(m, "gid=%d,", opts->gid);
+ if (opts->ttl)
+ fprintf(m, "ttl=%d,", opts->ttl);
+ if (*opts->nls_name)
+ fprintf(m, "iocharset=%s,", opts->nls_name);
+ if (flags & MS_NOSUID)
+ fprintf(m, "%s,", MNTOPT_NOSUID);
+ if (flags & MS_RDONLY)
+ fprintf(m, "%s,", MNTOPT_RO);
+ else
+ fprintf(m, "%s,", MNTOPT_RW);
+
+ fclose(m);
+
+ if (size > 0)
+ buf[size - 1] = 0;
+ else
+ buf = "defaults";
+
+ f = setmntent(MOUNTED, "a+");
+ if (!f)
+ {
+ rc = 2; /* Could not open mount table for update. */
+ }
+ else
+ {
+ e.mnt_fsname = (char*)host_name;
+ e.mnt_dir = (char*)mount_point;
+ e.mnt_type = "vboxsf";
+ e.mnt_opts = buf;
+ e.mnt_freq = 0;
+ e.mnt_passno = 0;
+
+ if (addmntent(f, &e))
+ rc = 3; /* Could not add an entry to the mount table. */
+
+ endmntent(f);
+ }
+
+ if (size > 0)
+ {
+ memset(buf, 0, size);
+ free(buf);
+ }
+
+ return rc;
+}
diff --git a/src/VBox/Additions/linux/sharedfolders/vbsfmount.h b/src/VBox/Additions/linux/sharedfolders/vbsfmount.h
new file mode 100644
index 00000000..c14e5284
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/vbsfmount.h
@@ -0,0 +1,90 @@
+/* $Id: vbsfmount.h $ */
+/** @file
+ * vboxsf - VBox Linux Shared Folders VFS, mount(2) parameter structure.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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.
+ */
+
+#ifndef GA_INCLUDED_SRC_linux_sharedfolders_vbsfmount_h
+#define GA_INCLUDED_SRC_linux_sharedfolders_vbsfmount_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Linux constraints the size of data mount argument to PAGE_SIZE - 1. */
+#define MAX_HOST_NAME 256
+#define MAX_NLS_NAME 32
+
+#define VBSF_MOUNT_SIGNATURE_BYTE_0 '\377'
+#define VBSF_MOUNT_SIGNATURE_BYTE_1 '\376'
+#define VBSF_MOUNT_SIGNATURE_BYTE_2 '\375'
+
+struct vbsf_mount_info_new {
+ /*
+ * The old version of the mount_info struct started with a
+ * char name[MAX_HOST_NAME] field, where name cannot be '\0'.
+ * So the new version of the mount_info struct starts with a
+ * nullchar field which is always 0 so that we can detect and
+ * reject the old structure being passed.
+ */
+ char nullchar;
+ char signature[3]; /* signature */
+ int length; /* length of the whole structure */
+ char name[MAX_HOST_NAME]; /* share name */
+ char nls_name[MAX_NLS_NAME]; /* name of an I/O charset */
+ int uid; /* user ID for all entries, default 0=root */
+ int gid; /* group ID for all entries, default 0=root */
+ int ttl; /* time to live */
+ int dmode; /* mode for directories if != 0xffffffff */
+ int fmode; /* mode for regular files if != 0xffffffff */
+ int dmask; /* umask applied to directories */
+ int fmask; /* umask applied to regular files */
+ char tag[32]; /**< Mount tag for VBoxService automounter. @since 6.0 */
+};
+
+struct vbsf_mount_opts {
+ int uid;
+ int gid;
+ int ttl;
+ int dmode;
+ int fmode;
+ int dmask;
+ int fmask;
+ int ronly;
+ int sloppy;
+ int noexec;
+ int nodev;
+ int nosuid;
+ int remount;
+ char nls_name[MAX_NLS_NAME];
+ char *convertcp;
+};
+
+/** Completes the mount operation by adding the new mount point to mtab if required. */
+int vbsfmount_complete(const char *host_name, const char *mount_point,
+ unsigned long flags, struct vbsf_mount_opts *opts);
+
+#endif /* !GA_INCLUDED_SRC_linux_sharedfolders_vbsfmount_h */
diff --git a/src/VBox/Additions/linux/sharedfolders/vfsmod.c b/src/VBox/Additions/linux/sharedfolders/vfsmod.c
new file mode 100644
index 00000000..209424a6
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/vfsmod.c
@@ -0,0 +1,683 @@
+/* $Id: vfsmod.c $ */
+/** @file
+ * vboxsf - VBox Linux Shared Folders VFS, module init/term, super block management.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+# include <linux/mount.h>
+#endif
+#include <linux/seq_file.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 >= RT_UOFFSETOF(struct vbsf_mount_info_new, tag)) {
+ /* 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;
+ }
+
+ if ((unsigned)info->length >= sizeof(struct vbsf_mount_info_new)) {
+ AssertCompile(sizeof(sf_g->tag) >= sizeof(info->tag));
+ memcpy(sf_g->tag, info->tag, sizeof(info->tag));
+ sf_g->tag[sizeof(sf_g->tag) - 1] = '\0';
+ } else {
+ sf_g->tag[0] = '\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 /* LINUX_VERSION_CODE >= 2.6.36 */
+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 /* LINUX_VERSION_CODE >= 2.6.36 */
+
+/* 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;
+ if ((unsigned)info->length >= RT_UOFFSETOF(struct vbsf_mount_info_new, tag)) {
+ 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;
+ }
+ if ((unsigned)info->length >= sizeof(struct vbsf_mount_info_new)) {
+ AssertCompile(sizeof(sf_g->tag) >= sizeof(info->tag));
+ memcpy(sf_g->tag, info->tag, sizeof(info->tag));
+ sf_g->tag[sizeof(sf_g->tag) - 1] = '\0';
+ } else {
+ sf_g->tag[0] = '\0';
+ }
+ }
+ }
+
+ 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 /* LINUX_VERSION_CODE < 2.4.23 */
+ return -ENOSYS;
+#endif /* LINUX_VERSION_CODE < 2.4.23 */
+}
+
+/** Show mount options. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+static int sf_show_options(struct seq_file *m, struct vfsmount *mnt)
+#else
+static int sf_show_options(struct seq_file *m, struct dentry *root)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ struct super_block *sb = mnt->mnt_sb;
+#else
+ struct super_block *sb = root->d_sb;
+#endif
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(sb);
+ if (sf_g) {
+ seq_printf(m, ",uid=%u,gid=%u,ttl=%u,dmode=0%o,fmode=0%o,dmask=0%o,fmask=0%o",
+ sf_g->uid, sf_g->gid, sf_g->ttl, sf_g->dmode, sf_g->fmode, sf_g->dmask, sf_g->fmask);
+ if (sf_g->tag[0] != '\0') {
+ seq_puts(m, ",tag=");
+ seq_escape(m, sf_g->tag, " \t\n\\");
+ }
+ }
+
+ return 0;
+}
+
+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,
+ .show_options = sf_show_options
+};
+
+#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 /* LINUX_VERSION_CODE >= 2.6.39 */
+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 /* LINUX_VERSION_CODE >= 2.6.39 */
+
+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 /* LINUX_VERSION_CODE >= 2.6.0 */
+
+#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;
diff --git a/src/VBox/Additions/linux/sharedfolders/vfsmod.h b/src/VBox/Additions/linux/sharedfolders/vfsmod.h
new file mode 100644
index 00000000..f7b71831
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/vfsmod.h
@@ -0,0 +1,181 @@
+/* $Id: vfsmod.h $ */
+/** @file
+ * vboxsf - Linux Shared Folders VFS, internal header.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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.
+ */
+
+#ifndef GA_INCLUDED_SRC_linux_sharedfolders_vfsmod_h
+#define GA_INCLUDED_SRC_linux_sharedfolders_vfsmod_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include "the-linux-kernel.h"
+#include <VBox/log.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+# include <linux/backing-dev.h>
+#endif
+
+#include <VBox/VBoxGuestLibSharedFolders.h>
+#include "vbsfmount.h"
+
+#define DIR_BUFFER_SIZE (16*_1K)
+
+/* per-shared folder information */
+struct sf_glob_info {
+ VBGLSFMAP map;
+ struct nls_table *nls;
+ int ttl;
+ int uid;
+ int gid;
+ int dmode;
+ int fmode;
+ int dmask;
+ int fmask;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ struct backing_dev_info bdi;
+#endif
+ char tag[32]; /**< Mount tag for VBoxService automounter. @since 6.0 */
+};
+
+/* per-inode information */
+struct sf_inode_info {
+ /* which file */
+ SHFLSTRING *path;
+ /* some information was changed, update data on next revalidate */
+ int force_restat;
+ /* directory content changed, update the whole directory on next sf_getdent */
+ int force_reread;
+ /* file structure, only valid between open() and release() */
+ struct file *file;
+ /* handle valid if a file was created with sf_create_aux until it will
+ * be opened with sf_reg_open() */
+ SHFLHANDLE handle;
+};
+
+struct sf_dir_info {
+ struct list_head info_list;
+};
+
+struct sf_dir_buf {
+ size_t cEntries;
+ size_t cbFree;
+ size_t cbUsed;
+ void *buf;
+ struct list_head head;
+};
+
+struct sf_reg_info {
+ SHFLHANDLE handle;
+};
+
+/* globals */
+extern VBGLSFCLIENT client_handle;
+
+/* forward declarations */
+extern struct inode_operations sf_dir_iops;
+extern struct inode_operations sf_lnk_iops;
+extern struct inode_operations sf_reg_iops;
+extern struct file_operations sf_dir_fops;
+extern struct file_operations sf_reg_fops;
+extern struct dentry_operations sf_dentry_ops;
+extern struct address_space_operations sf_reg_aops;
+
+extern void sf_init_inode(struct sf_glob_info *sf_g, struct inode *inode,
+ PSHFLFSOBJINFO info);
+extern int sf_stat(const char *caller, struct sf_glob_info *sf_g,
+ SHFLSTRING * path, PSHFLFSOBJINFO result, int ok_to_fail);
+extern int sf_inode_revalidate(struct dentry *dentry);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+extern int sf_getattr(const struct path *path, struct kstat *kstat,
+ u32 request_mask, unsigned int query_flags);
+# else
+extern int sf_getattr(struct vfsmount *mnt, struct dentry *dentry,
+ struct kstat *kstat);
+# endif
+extern int sf_setattr(struct dentry *dentry, struct iattr *iattr);
+#endif
+extern int sf_path_from_dentry(const char *caller, struct sf_glob_info *sf_g,
+ struct sf_inode_info *sf_i,
+ struct dentry *dentry, SHFLSTRING ** result);
+extern int sf_nlscpy(struct sf_glob_info *sf_g, char *name,
+ size_t name_bound_len, const unsigned char *utf8_name,
+ size_t utf8_len);
+extern void sf_dir_info_free(struct sf_dir_info *p);
+extern void sf_dir_info_empty(struct sf_dir_info *p);
+extern struct sf_dir_info *sf_dir_info_alloc(void);
+extern int sf_dir_read_all(struct sf_glob_info *sf_g,
+ struct sf_inode_info *sf_i, struct sf_dir_info *sf_d,
+ SHFLHANDLE handle);
+extern int sf_init_backing_dev(struct sf_glob_info *sf_g);
+extern void sf_done_backing_dev(struct sf_glob_info *sf_g);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+# define STRUCT_STATFS struct statfs
+#else
+# define STRUCT_STATFS struct kstatfs
+#endif
+int sf_get_volume_info(struct super_block *sb, STRUCT_STATFS * stat);
+
+#ifdef __cplusplus
+# define CMC_API __attribute__ ((cdecl, regparm (0)))
+#else
+# define CMC_API __attribute__ ((regparm (0)))
+#endif
+
+#define TRACE() LogFunc(("tracepoint\n"))
+
+/* Following casts are here to prevent assignment of void * to
+ pointers of arbitrary type */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+# define GET_GLOB_INFO(sb) ((struct sf_glob_info *) (sb)->u.generic_sbp)
+# define SET_GLOB_INFO(sb, sf_g) (sb)->u.generic_sbp = sf_g
+#else
+# define GET_GLOB_INFO(sb) ((struct sf_glob_info *) (sb)->s_fs_info)
+# define SET_GLOB_INFO(sb, sf_g) (sb)->s_fs_info = sf_g
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) || defined(KERNEL_FC6)
+/* FC6 kernel 2.6.18, vanilla kernel 2.6.19+ */
+# define GET_INODE_INFO(i) ((struct sf_inode_info *) (i)->i_private)
+# define SET_INODE_INFO(i, sf_i) (i)->i_private = sf_i
+#else
+/* vanilla kernel up to 2.6.18 */
+# define GET_INODE_INFO(i) ((struct sf_inode_info *) (i)->u.generic_ip)
+# define SET_INODE_INFO(i, sf_i) (i)->u.generic_ip = sf_i
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+# define GET_F_DENTRY(f) (f->f_path.dentry)
+#else
+# define GET_F_DENTRY(f) (f->f_dentry)
+#endif
+
+#endif /* !GA_INCLUDED_SRC_linux_sharedfolders_vfsmod_h */
diff --git a/src/VBox/Additions/linux/testcase/TimesyncBackdoor.c b/src/VBox/Additions/linux/testcase/TimesyncBackdoor.c
new file mode 100644
index 00000000..82fb82c8
--- /dev/null
+++ b/src/VBox/Additions/linux/testcase/TimesyncBackdoor.c
@@ -0,0 +1,93 @@
+/** @file
+ *
+ * VirtualBox Timesync using temporary Backdoor
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <unistd.h>
+#include <asm/io.h>
+#include <sys/time.h>
+#include <time.h>
+
+void usage()
+{
+ printf("TimesyncBackdoor [-interval <seconds>]"
+ " [-daemonize]"
+ "\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int secInterval = 10;
+ int fDaemonize = 0;
+ int i;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-interval") == 0)
+ {
+ if (argc <= i)
+ {
+ usage();
+ return 1;
+ }
+ secInterval = atoi(argv[i + 1]);
+ i++;
+ }
+ else if (strcmp(argv[i], "-daemonize") == 0)
+ {
+ fDaemonize = 1;
+ }
+ else
+ {
+ usage();
+ return 1;
+ }
+ }
+
+ /* get port IO permission */
+ if (iopl(3))
+ {
+ printf("Error: could not set IOPL to 3!\n");
+ return 1;
+ }
+
+ printf("VirtualBox timesync tool. Sync interval: %d seconds.\n", secInterval);
+
+ if (fDaemonize)
+ daemon(1, 0);
+
+ do
+ {
+ unsigned long long time;
+ /* get the high 32bit, this _must_ be done first */
+ outl(0, 0x505);
+ time = (unsigned long long)inl(0x505) << 32;
+ /* get the low 32bit */
+ outl(1, 0x505);
+ time |= inl(0x505);
+
+ /* set the system time */
+ struct timeval tv;
+ tv.tv_sec = time / (unsigned long long)1000;
+ tv.tv_usec = (time % (unsigned long long)1000) * 1000;
+ settimeofday(&tv, NULL);
+
+ /* wait for the next run */
+ sleep(secInterval);
+
+ } while (1);
+
+ return 0;
+}