diff --git a/Makefile.dep b/Makefile.dep
index 3b13db9cc661184a7bd7badc92afc2f4a74268f3..383bb7f0893e94bfdaba233d622fb1f15ca1b12b 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -668,6 +668,20 @@ ifneq (,$(filter ccn-lite,$(USEPKG)))
   USEMODULE += ccn-lite-utils
 endif
 
+ifneq (,$(filter fatfs_vfs,$(USEMODULE)))
+    USEPKG += fatfs
+    USEMODULE += vfs
+
+    ifeq ($(BOARD),native)
+      USEMODULE += fatfs_diskio_native
+      FATFS_DISKIO_NATIVE_DEFAULT_FILE ?= \"riot_fatfs_disk.img\"
+    else
+      USEMODULE += fatfs_diskio_sdcard_spi
+      USEMODULE += auto_init_storage
+    endif
+
+endif
+
 # always select gpio (until explicit dependencies are sorted out)
 FEATURES_OPTIONAL += periph_gpio
 
diff --git a/core/include/kernel_defines.h b/core/include/kernel_defines.h
index 6584935d8832f7eada4f6d8a032db0ff197f260b..d3fc3e86763d3c0ab69916620ea34db6936173f1 100644
--- a/core/include/kernel_defines.h
+++ b/core/include/kernel_defines.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2014 Freie Universität Berlin
+ *               2017 HAW-Hamburg
  *
  * This file is subject to the terms and conditions of the GNU Lesser
  * General Public License v2.1. See the file LICENSE in the top level
@@ -14,6 +15,7 @@
  * @brief       Common macros and compiler attributes/pragmas configuration
  *
  * @author      René Kijewski <rene.kijewski@fu-berlin.de>
+ * @author      Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
  */
 
 #ifndef KERNEL_DEFINES_H
@@ -114,6 +116,18 @@
  */
 #define ALIGN_OF(T) (offsetof(struct { char c; T t; }, t))
 
+/**
+ * @def         BUILD_BUG_ON(condition)
+ * @brief       Forces a compilation error if condition is true.
+ *              This trick is only needed if the condition can't be evaluated
+ *              before compile time (i.e. sizeof(sometype_t) < 42 )
+ *              For more details on this see for example:
+ *              https://git.kernel.org/pub/scm/linux/kernel/git/stable/
+ *              linux-stable.git/tree/include/linux/bug.h
+ * @param[in]   condition  A condition that will be evaluated at compile time
+ */
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)]))
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/cpu/native/mtd/mtd_native.c b/cpu/native/mtd/mtd_native.c
index 5e6089df94d116695e44f989469f81e6840ca0ec..5c3abaa0d632102c35bd73bda7a38e31ef25628e 100644
--- a/cpu/native/mtd/mtd_native.c
+++ b/cpu/native/mtd/mtd_native.c
@@ -37,7 +37,7 @@ static int _init(mtd_dev_t *dev)
     FILE *f = real_fopen(_dev->fname, "r");
 
     if (!f) {
-        DEBUG("mtd_native: init: creating file %s\n", name);
+        DEBUG("mtd_native: init: creating file %s\n", _dev->fname);
         f = real_fopen(_dev->fname, "w+");
         if (!f) {
             return -EIO;
diff --git a/pkg/fatfs/Makefile.include b/pkg/fatfs/Makefile.include
index 6579d73d0ea08f3834444d5784a1ca2870c0a924..2db484e79978733c334c5dbe8e53c0bdbd359ec4 100644
--- a/pkg/fatfs/Makefile.include
+++ b/pkg/fatfs/Makefile.include
@@ -12,6 +12,10 @@ ifneq (,$(filter fatfs_diskio_sdcard_spi,$(USEMODULE)))
   DIRS += $(RIOTBASE)/pkg/fatfs/fatfs_diskio/sdcard_spi
 endif
 
+ifneq (,$(filter fatfs_vfs,$(USEMODULE)))
+    DIRS += $(RIOTBASE)/pkg/fatfs/fatfs_vfs
+endif
+
 ifeq ($(shell uname -s),Darwin)
   CFLAGS += -Wno-empty-body
 endif
diff --git a/pkg/fatfs/fatfs_vfs/Makefile b/pkg/fatfs/fatfs_vfs/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..a74c941acb3c533dec23d4fe39dfdfae57bc31e7
--- /dev/null
+++ b/pkg/fatfs/fatfs_vfs/Makefile
@@ -0,0 +1,3 @@
+MODULE := fatfs_vfs
+
+include $(RIOTBASE)/Makefile.base
diff --git a/pkg/fatfs/fatfs_vfs/fatfs_vfs.c b/pkg/fatfs/fatfs_vfs/fatfs_vfs.c
new file mode 100644
index 0000000000000000000000000000000000000000..12fda2f7cb121e9a782756b2aa19c93c73683efa
--- /dev/null
+++ b/pkg/fatfs/fatfs_vfs/fatfs_vfs.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2017 HAW-Hamburg
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     fs
+ * @{
+ *
+ * @file
+ * @brief       FatFs wrapper for vfs
+ *
+ * @author      Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ *
+ * @}
+ */
+
+
+#include <fcntl.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <sys/stat.h> /* for struct stat */
+#include <string.h>
+
+#include "fs/fatfs.h"
+
+#include "kernel_defines.h" /* needed for BUILD_BUG_ON */
+#include "time.h"
+
+#define ENABLE_DEBUG (0)
+#include <debug.h>
+
+static int fatfs_err_to_errno(int32_t err);
+static void _fatfs_time_to_timespec(WORD fdate, WORD ftime, time_t *time);
+
+static int _mount(vfs_mount_t *mountp)
+{
+    /* if one of the lines below fail to compile you probably need to adjust
+       vfs buffer sizes ;) */
+    BUILD_BUG_ON(VFS_DIR_BUFFER_SIZE < sizeof(DIR));
+    BUILD_BUG_ON(VFS_FILE_BUFFER_SIZE < sizeof(fatfs_file_desc_t));
+
+    fatfs_desc_t *fs_desc = (fatfs_desc_t *)mountp->private_data;
+
+    char volume_str[FATFS_MAX_VOL_STR_LEN];
+    snprintf(volume_str, sizeof(volume_str), "%d:/", fs_desc->vol_idx);
+
+    memset(&fs_desc->fat_fs, 0, sizeof(fs_desc->fat_fs));
+
+    DEBUG("mounting file system of volume '%s'\n", volume_str);
+    FRESULT res = f_mount(&fs_desc->fat_fs, volume_str, 1);
+
+    if (res == FR_OK) {
+        DEBUG("[OK]");
+    }
+    else {
+        DEBUG("[ERROR]");
+    }
+
+    return fatfs_err_to_errno(res);
+}
+
+static int _umount(vfs_mount_t *mountp)
+{
+    fatfs_desc_t *fs_desc = mountp->private_data;
+
+    DEBUG("fatfs_vfs.c: _umount: private_data = %p\n", mountp->private_data);
+
+    char volume_str[FATFS_MAX_VOL_STR_LEN];
+    snprintf(volume_str, sizeof(volume_str), "%d:/", fs_desc->vol_idx);
+
+    DEBUG("unmounting file system of volume '%s'\n", volume_str);
+    FRESULT res = f_mount(NULL, volume_str, 1);
+
+    if (res == FR_OK) {
+        DEBUG("[OK]");
+        memset(&fs_desc->fat_fs, 0, sizeof(fs_desc->fat_fs));
+    }
+    else {
+        DEBUG("[ERROR]");
+    }
+
+    return fatfs_err_to_errno(res);
+}
+
+static int _unlink(vfs_mount_t *mountp, const char *name)
+{
+    char fatfs_abs_path[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
+    fatfs_desc_t *fs_desc = (fatfs_desc_t *)mountp->private_data;
+
+    snprintf(fatfs_abs_path, sizeof(fatfs_abs_path), "%d:/%s",
+             fs_desc->vol_idx, name);
+
+    return fatfs_err_to_errno(f_unlink(fatfs_abs_path));
+}
+
+static int _rename(vfs_mount_t *mountp, const char *from_path,
+                   const char *to_path)
+{
+    char fatfs_abs_path_f[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
+    char fatfs_abs_path_t[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
+    fatfs_desc_t *fs_desc = (fatfs_desc_t *)mountp->private_data;
+
+    snprintf(fatfs_abs_path_f, sizeof(fatfs_abs_path_f), "%d:/%s",
+             fs_desc->vol_idx, from_path);
+
+    snprintf(fatfs_abs_path_t, sizeof(fatfs_abs_path_t), "%d:/%s",
+             fs_desc->vol_idx, to_path);
+
+    return fatfs_err_to_errno(f_rename(fatfs_abs_path_f, fatfs_abs_path_t));
+}
+
+static int _open(vfs_file_t *filp, const char *name, int flags, mode_t mode,
+                 const char *abs_path)
+{
+    fatfs_file_desc_t *fd = (fatfs_file_desc_t *)&filp->private_data.buffer[0];
+    char fatfs_abs_path[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
+    fatfs_desc_t *fs_desc = (fatfs_desc_t *)filp->mp->private_data;
+    snprintf(fatfs_abs_path, sizeof(fatfs_abs_path), "%d:/%s", fs_desc->vol_idx,
+             name);
+
+    (void) abs_path;
+    (void) mode; /* fatfs can't use mode param with f_open*/
+    DEBUG("fatfs_vfs.c: _open: private_data = %p, name = %s; flags = 0x%x\n",
+          filp->mp->private_data, name, flags);
+
+    strncpy(fd->fname, fatfs_abs_path, VFS_NAME_MAX);
+
+    uint8_t fatfs_flags = 0;
+
+    if ((flags & O_ACCMODE) == O_RDONLY) {
+        fatfs_flags |= FA_READ;
+    }
+    if ((flags & O_ACCMODE) == O_WRONLY) {
+        fatfs_flags |= FA_WRITE;
+    }
+    if ((flags & O_ACCMODE) == O_RDWR) {
+        fatfs_flags |= (FA_READ | FA_WRITE);
+    }
+    if ((flags & O_APPEND) == O_APPEND) {
+        fatfs_flags |= FA_OPEN_APPEND;
+    }
+    if ((flags & O_TRUNC) == O_TRUNC) {
+        fatfs_flags |= FA_CREATE_ALWAYS;
+    }
+    if ((flags & O_CREAT) == O_CREAT) {
+        fatfs_flags |= FA_CREATE_NEW;
+    }
+    else {
+        fatfs_flags |= FA_OPEN_EXISTING;
+    }
+
+    FRESULT open_resu = f_open(&fd->file, fatfs_abs_path, fatfs_flags);
+    if (open_resu == FR_OK) {
+        DEBUG("[OK]");
+    }
+    else {
+        DEBUG("[ERROR]");
+    }
+
+    DEBUG("fatfs_vfs.c _open: returning fatfserr=%d; errno=%d\n", open_resu,
+          fatfs_err_to_errno(open_resu));
+
+    return fatfs_err_to_errno(open_resu);
+}
+
+static int _close(vfs_file_t *filp)
+{
+    fatfs_file_desc_t *fd = (fatfs_file_desc_t *)filp->private_data.buffer;
+
+    DEBUG("fatfs_vfs.c: _close: private_data = %p\n", filp->mp->private_data);
+
+    FRESULT res = f_close(&fd->file);
+
+    if (res == FR_OK) {
+        DEBUG("[OK]");
+    }
+    else {
+        DEBUG("[FAILED] (f_close error %d)\n", res);
+    }
+
+    return fatfs_err_to_errno(res);
+}
+
+static ssize_t _write(vfs_file_t *filp, const void *src, size_t nbytes)
+{
+    fatfs_file_desc_t *fd = (fatfs_file_desc_t *)filp->private_data.buffer;
+
+    UINT bw;
+
+    FRESULT res = f_write(&fd->file, src, nbytes, &bw);
+
+    if (res != FR_OK) {
+        return fatfs_err_to_errno(res);
+    }
+
+    return (ssize_t)bw;
+}
+
+static ssize_t _read(vfs_file_t *filp, void *dest, size_t nbytes)
+{
+    fatfs_file_desc_t *fd = (fatfs_file_desc_t *)filp->private_data.buffer;
+
+    UINT br;
+
+    FRESULT res = f_read(&fd->file, dest, nbytes, &br);
+
+
+    if (res != FR_OK) {
+        return fatfs_err_to_errno(res);
+    }
+
+    return (ssize_t)br;
+}
+
+static off_t _lseek(vfs_file_t *filp, off_t off, int whence)
+{
+    fatfs_file_desc_t *fd = (fatfs_file_desc_t *)filp->private_data.buffer;
+    FRESULT res;
+    off_t new_pos = 0;
+
+    if (whence == SEEK_SET) {
+        new_pos = off;
+    }
+    else if (whence == SEEK_CUR) {
+        new_pos = f_tell(&fd->file) + off;
+    }
+    else if (whence == SEEK_END) {
+        new_pos = f_size(&fd->file) + off;
+    }
+    else {
+        return fatfs_err_to_errno(FR_INVALID_PARAMETER);
+    }
+
+    res = f_lseek(&fd->file, new_pos);
+
+    if (res == FR_OK) {
+        return new_pos;
+    }
+
+    return fatfs_err_to_errno(res);
+}
+
+static int _fstat(vfs_file_t *filp, struct stat *buf)
+{
+    fatfs_file_desc_t *fd = (fatfs_file_desc_t *)filp->private_data.buffer;
+    char fatfs_abs_path[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
+    fatfs_desc_t *fs_desc = (fatfs_desc_t *)filp->mp->private_data;
+    FILINFO fi;
+    FRESULT res;
+
+    snprintf(fatfs_abs_path, sizeof(fatfs_abs_path), "%d:/%s", fs_desc->vol_idx,
+             fd->fname);
+
+    memset(buf, 0, sizeof(*buf));
+
+    res = f_stat(fatfs_abs_path, &fi);
+
+    if (res != FR_OK) {
+        return fatfs_err_to_errno(res);
+    }
+
+    buf->st_size = fi.fsize;
+
+    /* set last modification timestamp */
+    #ifdef SYS_STAT_H_
+    _fatfs_time_to_timespec(fi.fdate, fi.ftime, &(buf->st_mtim.tv_sec));
+    #else
+    _fatfs_time_to_timespec(fi.fdate, fi.ftime, &(buf->st_mtime));
+    #endif
+
+    if (fi.fattrib & AM_DIR) {
+        buf->st_mode = S_IFDIR;  /**< it's a directory */
+    }
+    else {
+        buf->st_mode = S_IFREG;  /**< it's a regular file */
+    }
+
+    /** always grant read access */
+    buf->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH);
+
+    if (fi.fattrib & AM_RDO) {
+        /** grant write access if file isn't RO*/
+        buf->st_mode ^= (S_IWUSR | S_IWGRP | S_IWOTH);
+    }
+
+    return fatfs_err_to_errno(res);
+}
+
+static int _opendir(vfs_DIR *dirp, const char *dirname, const char *abs_path)
+{
+    DIR *dir = (DIR *)&dirp->private_data.buffer;
+    char fatfs_abs_path[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
+    fatfs_desc_t *fs_desc = (fatfs_desc_t *)dirp->mp->private_data;
+    (void) abs_path;
+
+    snprintf(fatfs_abs_path, sizeof(fatfs_abs_path), "%d:/%s", fs_desc->vol_idx,
+             dirname);
+
+    return fatfs_err_to_errno(f_opendir(dir, fatfs_abs_path));
+}
+
+static int _readdir(vfs_DIR *dirp, vfs_dirent_t *entry)
+{
+    DIR *dir = (DIR *)&dirp->private_data.buffer[0];
+    FILINFO fi;
+
+    FRESULT res = f_readdir(dir, &fi);
+
+    if (res == FR_OK) {
+        if (fi.fname[0] == 0) {
+            return 0; /**< end of dir reached */
+        }
+        else {
+            entry->d_ino = 0; //TODO: set this properly
+            strncpy(entry->d_name, fi.fname, VFS_NAME_MAX);
+            return 1;
+        }
+    }
+
+    return fatfs_err_to_errno(res);
+}
+
+static int _closedir(vfs_DIR *dirp)
+{
+    DIR *dir = (DIR *)&dirp->private_data.buffer[0];
+
+    return fatfs_err_to_errno(f_closedir(dir));
+}
+
+static int _mkdir (vfs_mount_t *mountp, const char *name, mode_t mode)
+{
+    char fatfs_abs_path[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
+    fatfs_desc_t *fs_desc = (fatfs_desc_t *)mountp->private_data;
+    (void) mode;
+
+    snprintf(fatfs_abs_path, sizeof(fatfs_abs_path), "%d:/%s", fs_desc->vol_idx,
+             name);
+    return fatfs_err_to_errno(f_mkdir(fatfs_abs_path));
+}
+
+static int _rmdir (vfs_mount_t *mountp, const char *name)
+{
+    char fatfs_abs_path[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
+    fatfs_desc_t *fs_desc = (fatfs_desc_t *)mountp->private_data;
+
+    snprintf(fatfs_abs_path, sizeof(fatfs_abs_path), "%d:/%s", fs_desc->vol_idx,
+             name);
+    return fatfs_err_to_errno(f_unlink(fatfs_abs_path));
+}
+
+static void _fatfs_time_to_timespec(WORD fdate, WORD ftime, time_t *time)
+{
+    const uint16_t days_for_month[12] = { 0, 31, 59, 90, 120, 151, 181, 212,
+                                          243, 273, 304, 334 };
+
+    int year =  ((fdate >> 9) & 0x7F) + FATFS_YEAR_OFFSET - EPOCH_YEAR_OFFSET;
+    int month = ((fdate >> 5) & 0x0F) - 1;                      /**< 0..11 */
+    int day =    (fdate & 0x1F) - 1;                            /**< 0..31 */
+
+    int hour = (ftime >> 11) & 0x1F;                            /**< 0..23 */
+    int min  = (ftime >> 5) & 0x3F;                             /**< 0..59 */
+    int sec  = (ftime & 0x1F) * 2;                              /**< 0..58 */
+
+    /* leap years since 1970 */
+    int leap_years = ((year - 1 - EPOCH_YEAR_OFFSET - 2) / 4) -
+                     ((year - 1 - 1900) / 100) +
+                     ((year - 1 - 1600) / 400);
+
+    long days_since_epoch = (year - EPOCH_YEAR_OFFSET) * 365 +
+                            days_for_month[month] +
+                            leap_years +
+                            day;
+
+    if ((month > 2) &&
+        ((year % 4 == 0) &&
+         ((year % 100 != 0) || (year % 400 == 0)))) {
+        days_since_epoch++; /* add one day if this is a leap year */
+    }
+
+    *time = ((((days_since_epoch * 24) + hour) * 60) + min) * 60 + sec;
+}
+
+static int fatfs_err_to_errno(int32_t err)
+{
+    switch (err) {
+        case FR_OK:
+            return 0;
+        case FR_DISK_ERR:
+            return -EIO;
+        case FR_INT_ERR:
+            return -EIO;
+        case FR_NOT_READY:
+            return -ENODEV;
+        case FR_NO_FILE:
+            return -ENOENT;
+        case FR_NO_PATH:
+            return -ENOENT;
+        case FR_INVALID_NAME:
+            return -ENOENT;
+        case FR_DENIED:
+            return -EACCES;
+        case FR_EXIST:
+            return -EEXIST;
+        case FR_INVALID_OBJECT:
+        #ifdef EBADFD
+            return -EBADFD;
+        #else
+            return -EINVAL;
+        #endif
+        case FR_WRITE_PROTECTED:
+            return -EACCES;
+        case FR_INVALID_DRIVE:
+            return -ENXIO;
+        case FR_NOT_ENABLED:
+            return -ENODEV;
+        case FR_NO_FILESYSTEM:
+            return -ENODEV;
+        case FR_MKFS_ABORTED:
+            return -EINVAL;
+        case FR_TIMEOUT:
+            return -EBUSY;
+        case FR_LOCKED:
+            return -EACCES;
+        case FR_NOT_ENOUGH_CORE:
+            return -ENOMEM;
+        case FR_TOO_MANY_OPEN_FILES:
+            return -ENFILE;
+        case FR_INVALID_PARAMETER:
+            return -EINVAL;
+    }
+
+    return (int) err;
+}
+
+static const vfs_file_system_ops_t fatfs_fs_ops = {
+    .mount = _mount,
+    .umount = _umount,
+    .rename = _rename,
+    .unlink = _unlink,
+    .mkdir = _mkdir,
+    .rmdir = _rmdir,
+};
+
+static const vfs_file_ops_t fatfs_file_ops = {
+    .open = _open,
+    .close = _close,
+    .read = _read,
+    .write = _write,
+    .lseek = _lseek,
+    .fstat = _fstat,
+};
+
+static const vfs_dir_ops_t fatfs_dir_ops = {
+    .opendir = _opendir,
+    .readdir = _readdir,
+    .closedir = _closedir,
+};
+
+const vfs_file_system_t fatfs_file_system = {
+    .fs_op = &fatfs_fs_ops,
+    .f_op = &fatfs_file_ops,
+    .d_op = &fatfs_dir_ops,
+};
diff --git a/sys/include/fs/fatfs.h b/sys/include/fs/fatfs.h
new file mode 100644
index 0000000000000000000000000000000000000000..683959c945b688d769bfd6daea25e2cacb88f37a
--- /dev/null
+++ b/sys/include/fs/fatfs.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 HAW-Hamburg
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @defgroup    sys_fatfs  FatFs integration
+ * @ingroup     sys_fs
+ * @{
+ *
+ * @file
+ * @brief       FatFs integration for vfs
+ *
+ * @author      Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ */
+
+#ifndef FATFS_H
+#define FATFS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef VFS_DIR_BUFFER_SIZE
+#define VFS_DIR_BUFFER_SIZE (44)
+#endif
+
+#ifndef VFS_FILE_BUFFER_SIZE
+#define VFS_FILE_BUFFER_SIZE (72)
+#endif
+
+#include "fatfs/ff.h"
+#include "vfs.h"
+
+#ifndef FATFS_YEAR_OFFSET
+#define FATFS_YEAR_OFFSET (1980)
+#endif
+
+#define EPOCH_YEAR_OFFSET (1970)
+
+/** Size of the buffer needed for directory -> should be: sizeof(DIR)*/
+#define FATFS_DIR_SIZE (44)
+/** the problem with the above is: it's not possible to use sizeof(DIR) as this is later usen in #if (see below) */
+
+/** Size of the buffer needed for directory -> should be: sizeof(fatfs_file_desc_t)*/
+#define FATFS_FILE_SIZE (72)
+
+#define FATFS_MAX_VOL_STR_LEN (4)   /**< size needed for volume strings like "n:/" where n is the volume id */
+#define FATFS_MOUNT_OPT       (1)   /**< 0:mount on first file access, 1 mount in f_mount() call */
+
+#if (VFS_DIR_BUFFER_SIZE < FATFS_DIR_SIZE)
+#error "VFS_DIR_BUFFER_SIZE too small"
+#endif
+
+#if (VFS_FILE_BUFFER_SIZE < FATFS_FILE_SIZE)
+#error "VFS_FILE_BUFFER_SIZE too small"
+#endif
+
+/**
+ * needed info to run a FatFs instance
+ */
+typedef struct fatfs_desc {
+    FATFS fat_fs;       /**< FatFs work area needed for each volume */
+    uint8_t vol_idx;    /**< low level device that is used by FatFs */
+} fatfs_desc_t;
+
+/**
+ * info of a single opened file
+ */
+typedef struct fatfs_file_desc {
+    FIL file;                       /**< FatFs work area for a single file */
+    char fname[VFS_NAME_MAX + 1]; /**< name of the file (some FatFs functions e.g. f_stat use filename instead of FIL) */
+} fatfs_file_desc_t;
+
+/** The FatFs vfs driver, a pointer to a fatfs_desc_t must be provided as vfs_mountp::private_data */
+extern const vfs_file_system_t fatfs_file_system;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FATFS_H */
+
+/** @} */
diff --git a/sys/include/vfs.h b/sys/include/vfs.h
index 9b2beac45ff1533627d9f2d3c65c616c8da4ff2a..163c5c027f829c0d3b1aade522bf1ed1fe1f403c 100644
--- a/sys/include/vfs.h
+++ b/sys/include/vfs.h
@@ -119,6 +119,29 @@ extern "C" {
 #define VFS_DIR_BUFFER_SIZE (12)
 #endif
 
+#ifndef VFS_FILE_BUFFER_SIZE
+/**
+ * @brief Size of buffer space in vfs_file_t
+ *
+ * Same as with VFS_DIR_BUFFER_SIZE some file systems (e.g. FatFs) require more space
+ * to store data about their files.
+ *
+ *
+ * Guidelines are same as with VFS_DIR_BUFFER_SIZE, so add the following snippet
+ * to your fs header:
+ *
+ * @attention @code
+ * #if VFS_FILE_BUFFER_SIZE < 123
+ * #error VFS_FILE_BUFFER_SIZE is too small, at least 123 bytes is required
+ * #endif
+ * @endcode
+ *
+ * @attention Put the check in the public header file (.h), do not put the check in the
+ * implementation (.c) file.
+ */
+#define VFS_FILE_BUFFER_SIZE (1)
+#endif
+
 #ifndef VFS_NAME_MAX
 /**
  * @brief Maximum length of the name in a @c vfs_dirent_t (not including terminating null)
@@ -192,6 +215,7 @@ typedef struct {
     union {
         void *ptr;              /**< pointer to private data */
         int value;              /**< alternatively, you can use private_data as an int */
+        uint8_t buffer[VFS_FILE_BUFFER_SIZE]; /**< Buffer space, in case a single pointer is not enough */
     } private_data;             /**< File system driver private data, implementation defined */
 } vfs_file_t;
 
diff --git a/tests/pkg_fatfs/create_fat_image_file.sh b/tests/pkg_fatfs/create_fat_image_file.sh
new file mode 100755
index 0000000000000000000000000000000000000000..fa72597b7b2284bf680280596bde609741849806
--- /dev/null
+++ b/tests/pkg_fatfs/create_fat_image_file.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+#
+# Copyright (C) 2017 HAW-Hamburg
+#
+# This file is subject to the terms and conditions of the GNU Lesser
+# General Public License v2.1. See the file LICENSE in the top level
+# directory for more details.
+#
+dd if=/dev/zero of=riot_fatfs_disk.img bs=1M count=$1
+mkfs.fat riot_fatfs_disk.img
+sudo mkdir -p /media/riot_fatfs_disk
+sudo mount -o loop,umask=000 riot_fatfs_disk.img /media/riot_fatfs_disk
+touch /media/riot_fatfs_disk/test.txt
+echo "the test file content 123 abc" | tr '\n' '\0' >> /media/riot_fatfs_disk/test.txt
+sudo umount /media/riot_fatfs_disk
+tar -cjf riot_fatfs_disk.tar.gz riot_fatfs_disk.img
+rm riot_fatfs_disk.img
diff --git a/tests/pkg_fatfs/riot_fatfs_disk.tar.gz b/tests/pkg_fatfs/riot_fatfs_disk.tar.gz
new file mode 100644
index 0000000000000000000000000000000000000000..d7835b9db7aad654402d36e3cdbb6f40b285fbde
Binary files /dev/null and b/tests/pkg_fatfs/riot_fatfs_disk.tar.gz differ
diff --git a/tests/pkg_fatfs_vfs/Makefile b/tests/pkg_fatfs_vfs/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..44b562dd44198afde9fe3362fb84bf238c809bc2
--- /dev/null
+++ b/tests/pkg_fatfs_vfs/Makefile
@@ -0,0 +1,34 @@
+APPLICATION = pkg_fatfs_vfs
+include ../Makefile.tests_common
+
+USEMODULE += fatfs_vfs
+
+FATFS_DISKIO_NATIVE_DEFAULT_FILE ?= \"./bin/riot_fatfs_disk.img\"
+
+# whitelist can be removed when the problem described in #6063 was resolved
+# this list is composed of boards that support spi + native
+BOARD_WHITELIST := native airfy-beacon arduino-due arduino-duemilanove arduino-mega2560 \
+                   arduino-uno arduino-zero avsextrem cc2538dk fox frdm-k64f iotlab-a8-m3 \
+                   iotlab-m3 limifrog-v1 maple-mini msb-430 msb-430h msba2 msbiot mulle \
+                   nrf52840dk nrf52dk nrf6310 nucleo-f072 nucleo-f091 nucleo-f103 \
+                   nucleo-f302 nucleo-f303 nucleo-f334 nucleo-f401 nucleo-f410 nucleo-f411 \
+                   nucleo-f446 nucleo-l053 nucleo-l073 nucleo-l1 nucleo-l476 nucleo144-f207 \
+                   nucleo144-f303 nucleo144-f413 nucleo144-f429 nucleo144-f446 nucleo32-f031 \
+                   nucleo32-f042 nucleo32-f303 nucleo32-l031 nucleo32-l432 openmote-cc2538 \
+                   pba-d-01-kw2x pca10005 remote-pa remote-reva remote-revb samd21-xpro \
+                   saml21-xpro samr21-xpro sodaq-autonomo spark-core stm32f0discovery \
+                   stm32f3discovery stm32f4discovery telosb udoo waspmote-pro weio \
+                   wsn430-v1_3b wsn430-v1_4 yunjia-nrf51822 z1
+
+# export is needed to pass this value to the Makefile of the native_diskio module
+export FATFS_DISKIO_NATIVE_DEFAULT_FILE
+
+include $(RIOTBASE)/Makefile.include
+
+#this generates a compressed fat image file that can be used by the fat driver on native
+fatimage:
+	@./create_fat_image_file.sh
+
+test:
+	@tar -xjf riot_fatfs_disk.tar.gz -C ./bin/
+	./tests/01-run.py
diff --git a/tests/pkg_fatfs_vfs/create_fat_image_file.sh b/tests/pkg_fatfs_vfs/create_fat_image_file.sh
new file mode 100755
index 0000000000000000000000000000000000000000..517c6b366717eb86d846bf4dcc0900e94b6e1680
--- /dev/null
+++ b/tests/pkg_fatfs_vfs/create_fat_image_file.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+#
+# Copyright (C) 2017 HAW-Hamburg
+#
+# This file is subject to the terms and conditions of the GNU Lesser
+# General Public License v2.1. See the file LICENSE in the top level
+# directory for more details.
+#
+dd if=/dev/zero of=riot_fatfs_disk.img bs=1M count=128
+mkfs.fat riot_fatfs_disk.img
+sudo mkdir -p /media/riot_fatfs_disk
+sudo mount -o loop,umask=000 riot_fatfs_disk.img /media/riot_fatfs_disk
+touch /media/riot_fatfs_disk/test.txt
+echo "the test file content 123 abc" | tr '\n' '\0' >> /media/riot_fatfs_disk/test.txt
+sudo umount /media/riot_fatfs_disk
+tar -cjf riot_fatfs_disk.tar.gz riot_fatfs_disk.img
+rm riot_fatfs_disk.img
diff --git a/tests/pkg_fatfs_vfs/main.c b/tests/pkg_fatfs_vfs/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..4ba369b4bd210cdae6d124e46fe5f84945d281c1
--- /dev/null
+++ b/tests/pkg_fatfs_vfs/main.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2017 HAW-Hamburg
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     tests
+ * @{
+ *
+ * @file
+ * @brief       test application for using fatfs with vfs
+ *
+ * @author      Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ * @}
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+#include "fs/fatfs.h"
+#include "vfs.h"
+
+#if FATFS_FFCONF_OPT_FS_NORTC == 0
+#include "periph/rtc.h"
+#endif
+
+#define MNT_PATH  "/test"
+#define FNAME1 "TEST.TXT"
+#define FNAME2 "NEWFILE.TXT"
+#define FNAME_RNMD  "RENAMED.TXT"
+#define FNAME_NXIST "NOFILE.TXT"
+#define FULL_FNAME1 (MNT_PATH "/" FNAME1)
+#define FULL_FNAME2 (MNT_PATH "/" FNAME2)
+#define FULL_FNAME_RNMD  (MNT_PATH "/" FNAME_RNMD)
+#define FULL_FNAME_NXIST (MNT_PATH "/" FNAME_NXIST)
+#define DIR_NAME "SOMEDIR"
+
+static const char test_txt[]  = "the test file content 123 abc";
+static const char test_txt2[] = "another text";
+static const char test_txt3[] = "hello world for vfs";
+
+static fatfs_desc_t fatfs = {
+    .vol_idx = 0
+};
+
+static vfs_mount_t _test_vfs_mount = {
+    .mount_point = MNT_PATH,
+    .fs = &fatfs_file_system,
+    .private_data = (void *)&fatfs,
+};
+
+static void print_test_result(const char *test_name, int ok)
+{
+    printf("%s:[%s]\n", test_name, ok ? "OK" : "FAILED");
+}
+
+static void test_mount(void)
+{
+    print_test_result("test_mount__mount", vfs_mount(&_test_vfs_mount) == 0);
+    print_test_result("test_mount__umount", vfs_umount(&_test_vfs_mount) == 0);
+}
+
+static void test_open(void)
+{
+    int fd;
+    print_test_result("test_open__mount", vfs_mount(&_test_vfs_mount) == 0);
+
+    /* try to open file that doesn't exist */
+    fd = vfs_open(FULL_FNAME_NXIST, O_RDONLY, 0);
+    print_test_result("test_open__open", fd == -ENOENT);
+
+    /* open file with RO, WO and RW access */
+    fd = vfs_open(FULL_FNAME1, O_RDONLY, 0);
+    print_test_result("test_open__open_ro", fd >= 0);
+    print_test_result("test_open__close_ro", vfs_close(fd) == 0);
+    fd = vfs_open(FULL_FNAME1, O_WRONLY, 0);
+    print_test_result("test_open__open_wo", fd >= 0);
+    print_test_result("test_open__close_wo", vfs_close(fd) == 0);
+    fd = vfs_open(FULL_FNAME1, O_RDWR, 0);
+    print_test_result("test_open__open_rw", fd >= 0);
+    print_test_result("test_open__close_rw", vfs_close(fd) == 0);
+
+    print_test_result("test_open__umount", vfs_umount(&_test_vfs_mount) == 0);
+}
+
+static void test_rw(void)
+{
+    char buf[sizeof(test_txt) + sizeof(test_txt2)];
+    int fd;
+    ssize_t nr, nw;
+    off_t new_pos;
+
+    print_test_result("test_rw__mount", vfs_mount(&_test_vfs_mount) == 0);
+
+    fd = vfs_open(FULL_FNAME1, O_RDONLY, 0);
+    print_test_result("test_rw__open_ro", fd >= 0);
+
+    /* compare file content with expected value */
+    memset(buf, 0, sizeof(buf));
+    nr = vfs_read(fd, buf, sizeof(test_txt));
+    print_test_result("test_rw__read_ro", (nr == sizeof(test_txt)) &&
+                      (strncmp(buf, test_txt, sizeof(test_txt)) == 0));
+
+    /* try to write to RO file (success if no bytes are actually written) */
+    nw = vfs_write(fd, test_txt2, sizeof(test_txt2));
+    print_test_result("test_rw__write_ro", nw <= 0);
+    print_test_result("test_rw__close_ro", vfs_close(fd) == 0);
+
+    fd = vfs_open("/test/test.txt", O_WRONLY, 0);
+    print_test_result("test_rw__open_wo", fd >= 0);
+
+    /* try to read from WO file (success if no bytes are actually read) */
+    nr = vfs_read(fd, buf, sizeof(test_txt));
+    print_test_result("test_rw__read_wo", nw <= 0);
+
+    print_test_result("test_rw__close_wo", vfs_close(fd) == 0);
+
+    memset(buf, 0, sizeof(buf));
+    fd = vfs_open(FULL_FNAME1, O_RDWR, 0);
+    print_test_result("test_rw__open_rw", fd >= 0);
+
+    /* read file content and compare it to the expected value */
+    nr = vfs_read(fd, buf, sizeof(test_txt));
+    print_test_result("test_rw__read_rw", (nr == sizeof(test_txt)) &&
+                      (strncmp(buf, test_txt, sizeof(test_txt)) == 0));
+
+    /* write to the file (text should be appended to the end of file) */
+    nw = vfs_write(fd, test_txt2, sizeof(test_txt2));
+    print_test_result("test_rw__write_rw", nw == sizeof(test_txt2));
+
+    /* seek to start of file */
+    new_pos = vfs_lseek(fd, 0, SEEK_SET);
+    print_test_result("test_rw__lseek", new_pos == 0);
+
+    /* read file content and compare to expected value */
+    memset(buf, 0, sizeof(buf));
+    nr = vfs_read(fd, buf, sizeof(buf));
+    print_test_result("test_rw__read_rw", (nr == sizeof(buf)) &&
+                      (strncmp(buf, test_txt, sizeof(test_txt)) == 0) &&
+                      (strncmp(&buf[sizeof(test_txt)],
+                               test_txt2,
+                               sizeof(test_txt2)) == 0));
+
+
+    print_test_result("test_rw__close_rw", vfs_close(fd) == 0);
+
+    /* create new file */
+    fd = vfs_open(FULL_FNAME2, O_RDWR | O_CREAT, 0);
+    print_test_result("test_rw__open_rwc", fd >= 0);
+
+    /* write to the new file, read it's content and compare to expected value */
+    nw = vfs_write(fd, test_txt3, sizeof(test_txt3));
+    print_test_result("test_rw__write_rwc", nw == sizeof(test_txt3));
+
+    new_pos = vfs_lseek(fd, 0, SEEK_SET);
+    print_test_result("test_rw__lseek_rwc", new_pos == 0);
+
+    memset(buf, 0, sizeof(buf));
+    nr = vfs_read(fd, buf, sizeof(test_txt3));
+    print_test_result("test_rw__read_rwc", (nr == sizeof(test_txt3)) &&
+                      (strncmp(buf, test_txt3, sizeof(test_txt3)) == 0));
+
+    print_test_result("test_rw__close_rwc", vfs_close(fd) == 0);
+    print_test_result("test_rw__umount", vfs_umount(&_test_vfs_mount) == 0);
+}
+
+static void test_dir(void)
+{
+    vfs_DIR dir;
+    vfs_dirent_t entry;
+    vfs_dirent_t entry2;
+
+    print_test_result("test_dir__mount", vfs_mount(&_test_vfs_mount) == 0);
+    print_test_result("test_dir__opendir", vfs_opendir(&dir, MNT_PATH) == 0);
+    print_test_result("test_dir__readdir1", vfs_readdir(&dir, &entry) == 1);
+    print_test_result("test_dir__readdir2", vfs_readdir(&dir, &entry2) == 1);
+
+    print_test_result("test_dir__readdir_name",
+                      ((strncmp(FNAME1, entry.d_name, sizeof(FNAME1)) == 0) &&
+                       (strncmp(FNAME2, entry2.d_name, sizeof(FNAME2)) == 0))
+                      ||
+                      ((strncmp(FNAME1, entry2.d_name, sizeof(FNAME1)) == 0) &&
+                       (strncmp(FNAME2, entry.d_name, sizeof(FNAME2)) == 0)));
+
+    print_test_result("test_dir__readdir3", vfs_readdir(&dir, &entry2) == 0);
+    print_test_result("test_dir__closedir", vfs_closedir(&dir) == 0);
+    print_test_result("test_dir__umount", vfs_umount(&_test_vfs_mount) == 0);
+}
+
+
+static void test_rename(void)
+{
+    vfs_DIR dir;
+    vfs_dirent_t entry;
+    vfs_dirent_t entry2;
+
+    print_test_result("test_rename__mount", vfs_mount(&_test_vfs_mount) == 0);
+
+    print_test_result("test_rename__rename",
+                      vfs_rename(FULL_FNAME1, FULL_FNAME_RNMD) == 0);
+
+    print_test_result("test_rename__opendir", vfs_opendir(&dir, MNT_PATH) == 0);
+    print_test_result("test_rename__readdir1", vfs_readdir(&dir, &entry) == 1);
+    print_test_result("test_rename__readdir2", vfs_readdir(&dir, &entry2) == 1);
+
+    print_test_result("test_rename__check_name",
+              ((strncmp(FNAME_RNMD, entry.d_name, sizeof(FNAME_RNMD)) == 0) &&
+               (strncmp(FNAME2, entry2.d_name, sizeof(FNAME2)) == 0))
+              ||
+              ((strncmp(FNAME_RNMD, entry2.d_name, sizeof(FNAME_RNMD)) == 0) &&
+               (strncmp(FNAME2, entry.d_name, sizeof(FNAME2)) == 0)));
+
+    print_test_result("test_rename__readdir3", vfs_readdir(&dir, &entry2) == 0);
+    print_test_result("test_rename__closedir", vfs_closedir(&dir) == 0);
+    print_test_result("test_rename__umount", vfs_umount(&_test_vfs_mount) == 0);
+}
+
+static void test_unlink(void)
+{
+    vfs_DIR dir;
+    vfs_dirent_t entry;
+
+    print_test_result("test_unlink__mount", vfs_mount(&_test_vfs_mount) == 0);
+    print_test_result("test_unlink__unlink1", vfs_unlink(FULL_FNAME2) == 0);
+    print_test_result("test_unlink__unlink2", vfs_unlink(FULL_FNAME_RNMD) == 0);
+    print_test_result("test_unlink__opendir", vfs_opendir(&dir, MNT_PATH) == 0);
+    print_test_result("test_unlink__readdir", vfs_readdir(&dir, &entry) == 0);
+    print_test_result("test_unlink__closedir", vfs_closedir(&dir) == 0);
+    print_test_result("test_unlink__umount", vfs_umount(&_test_vfs_mount) == 0);
+}
+
+static void test_mkrmdir(void)
+{
+    vfs_DIR dir;
+
+    print_test_result("test_mkrmdir__mount", vfs_mount(&_test_vfs_mount) == 0);
+
+    print_test_result("test_mkrmdir__mkdir",
+                      vfs_mkdir(MNT_PATH"/"DIR_NAME, 0) == 0);
+
+    print_test_result("test_mkrmdir__opendir1",
+                      vfs_opendir(&dir, MNT_PATH"/"DIR_NAME) == 0);
+
+    print_test_result("test_mkrmdir__closedir", vfs_closedir(&dir) == 0);
+
+    print_test_result("test_mkrmdir__rmdir",
+                      vfs_rmdir(MNT_PATH"/"DIR_NAME) == 0);
+
+    print_test_result("test_mkrmdir__opendir2",
+                      vfs_opendir(&dir, MNT_PATH"/"DIR_NAME) < 0);
+
+    print_test_result("test_mkrmdir__umount",
+                      vfs_umount(&_test_vfs_mount) == 0);
+}
+
+static void test_create(void)
+{
+    int fd;
+    ssize_t nw;
+    print_test_result("test_create__mount", vfs_mount(&_test_vfs_mount) == 0);
+
+    fd = vfs_open(FULL_FNAME1, O_WRONLY | O_CREAT, 0);
+    print_test_result("test_create__open_wo", fd >= 0);
+
+    nw = vfs_write(fd, test_txt, sizeof(test_txt));
+    print_test_result("test_create__write_wo", nw == sizeof(test_txt));
+    print_test_result("test_create__close_wo", vfs_close(fd) == 0);
+    print_test_result("test_create__umount", vfs_umount(&_test_vfs_mount) == 0);
+}
+
+
+int main(void)
+{
+    #if FATFS_FFCONF_OPT_FS_NORTC == 0
+    rtc_init();
+    #endif
+
+    printf("Tests for FatFs over VFS - test results will be printed "
+           "in the format test_name:result\n");
+
+    test_mount();
+    test_open();
+    test_rw();
+    test_dir();
+    test_rename();
+    test_unlink();
+    test_mkrmdir();
+    test_create();
+
+    printf("Test end.\n");
+
+    return 0;
+}
diff --git a/tests/pkg_fatfs_vfs/riot_fatfs_disk.tar.gz b/tests/pkg_fatfs_vfs/riot_fatfs_disk.tar.gz
new file mode 100644
index 0000000000000000000000000000000000000000..7bd72ebc2c5e4fd2bf84022936bafa730a40b834
Binary files /dev/null and b/tests/pkg_fatfs_vfs/riot_fatfs_disk.tar.gz differ
diff --git a/tests/pkg_fatfs_vfs/tests/01-run.py b/tests/pkg_fatfs_vfs/tests/01-run.py
new file mode 100755
index 0000000000000000000000000000000000000000..6150bbc8c545fc877993c08d9f489dfc2d3fc559
--- /dev/null
+++ b/tests/pkg_fatfs_vfs/tests/01-run.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2017 HAW-Hamburg.de
+#
+# This file is subject to the terms and conditions of the GNU Lesser
+# General Public License v2.1. See the file LICENSE in the top level
+# directory for more details.
+
+import os
+import sys
+
+sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
+import testrunner
+
+from datetime import datetime
+
+class TestFailed(Exception):
+    pass
+
+def testfunc(child):
+
+    child.expect(u"Tests for FatFs over VFS - test results will be printed in "
+                  "the format test_name:result\r\n")
+
+    while True:
+        res = child.expect([u"[^\n]*:\[OK\]\r\n",
+                            u"Test end.\r\n",
+                            u".[^\n]*:\[FAILED\]\r\n" ,
+                            u".*\r\n"])
+        if res > 1:
+            raise TestFailed(child.after.split(':',1)[0] + " test failed!")
+        elif res == 1:
+            break;
+
+if __name__ == "__main__":
+    sys.exit(testrunner.run(testfunc))