mountpoint.c revision 26bedc1f8aace67ff8f19f625a403a097017845a
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2006-2010 Dovecot authors, see the included COPYING file */
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#include "lib.h"
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#include "mountpoint.h"
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#include <sys/stat.h>
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen#ifdef HAVE_SYS_VMOUNT_H
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen# include <stdio.h>
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen# include <sys/vmount.h> /* AIX */
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen#elif defined(HAVE_STATVFS_MNTFROMNAME)
f3e1593a7d9b02090575fb20db90a235e10145a5Timo Sirainen# include <sys/statvfs.h> /* NetBSD 3.0+, FreeBSD 5.0+ */
f3e1593a7d9b02090575fb20db90a235e10145a5Timo Sirainen# define STATVFS_STR "statvfs"
6daca8888bbf2b5bf26903cf397d5219ea752241Timo Sirainen#elif defined(HAVE_STATFS_MNTFROMNAME)
f3e1593a7d9b02090575fb20db90a235e10145a5Timo Sirainen# include <sys/param.h> /* Older BSDs */
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen# include <sys/mount.h>
f3e1593a7d9b02090575fb20db90a235e10145a5Timo Sirainen# define statvfs statfs
f3e1593a7d9b02090575fb20db90a235e10145a5Timo Sirainen# define STATVFS_STR "statfs"
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#elif defined(HAVE_MNTENT_H)
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen# include <stdio.h>
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen# include <mntent.h> /* Linux */
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#elif defined(HAVE_SYS_MNTTAB_H)
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen# include <stdio.h>
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen# include <sys/mnttab.h> /* Solaris */
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen# include <sys/mntent.h>
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#else
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen# define MOUNTPOINT_UNKNOWN
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#endif
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen
acc8c0647873b1c847bfa362ddefd0d219d0aa91Timo Sirainen#ifdef HAVE_SYS_MNTTAB_H
d8eedfaad386a8776e4931086b039b72e1ad38c4Timo Sirainen# define MTAB_PATH MNTTAB /* Solaris */
acc8c0647873b1c847bfa362ddefd0d219d0aa91Timo Sirainen#else
acc8c0647873b1c847bfa362ddefd0d219d0aa91Timo Sirainen# define MTAB_PATH "/etc/mtab" /* Linux */
acc8c0647873b1c847bfa362ddefd0d219d0aa91Timo Sirainen#endif
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen/* AIX doesn't have these defined */
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#ifndef MNTTYPE_SWAP
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen# define MNTTYPE_SWAP "swap"
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#endif
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#ifndef MNTTYPE_IGNORE
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen# define MNTTYPE_IGNORE "ignore"
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#endif
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen#ifndef MNTTYPE_JFS
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen# define MNTTYPE_JFS "jfs"
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen#endif
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen#ifndef MNTTYPE_NFS
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen# define MNTTYPE_NFS "nfs"
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen#endif
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen
26bedc1f8aace67ff8f19f625a403a097017845aTimo Sirainen/* Linux sometimes has mtab entry for "rootfs" as well as the real root
26bedc1f8aace67ff8f19f625a403a097017845aTimo Sirainen entry. Skip the rootfs. */
26bedc1f8aace67ff8f19f625a403a097017845aTimo Sirainen#ifndef MNTTYPE_ROOTFS
26bedc1f8aace67ff8f19f625a403a097017845aTimo Sirainen# define MNTTYPE_ROOTFS "rootfs"
26bedc1f8aace67ff8f19f625a403a097017845aTimo Sirainen#endif
2dc6cf8bad599bfe9129bb496539a08ee3631cc0Timo Sirainen
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainenint mountpoint_get(const char *path, pool_t pool, struct mountpoint *point_r)
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen{
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#ifdef MOUNTPOINT_UNKNOWN
e6bdf53eb0143af99e3eb977ff0f8a496ecd1a8dTimo Sirainen memset(point_r, 0, sizeof(*point_r));
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen errno = ENOSYS;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen return -1;
f3e1593a7d9b02090575fb20db90a235e10145a5Timo Sirainen#elif defined (HAVE_STATFS_MNTFROMNAME) || defined(HAVE_STATVFS_MNTFROMNAME)
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen /* BSDs */
f3e1593a7d9b02090575fb20db90a235e10145a5Timo Sirainen struct statvfs buf;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen memset(point_r, 0, sizeof(*point_r));
f3e1593a7d9b02090575fb20db90a235e10145a5Timo Sirainen if (statvfs(path, &buf) < 0) {
9446c7a5d400cba60d097c528bd08312552438e3Timo Sirainen if (errno == ENOENT)
9446c7a5d400cba60d097c528bd08312552438e3Timo Sirainen return 0;
9446c7a5d400cba60d097c528bd08312552438e3Timo Sirainen
f3e1593a7d9b02090575fb20db90a235e10145a5Timo Sirainen i_error(STATVFS_STR"(%s) failed: %m", path);
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen return -1;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen }
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen point_r->device_path = p_strdup(pool, buf.f_mntfromname);
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen point_r->mount_path = p_strdup(pool, buf.f_mntonname);
aff1e150e13980354cfd794c74dac76a791a641eTimo Sirainen#ifdef __osf__ /* Tru64 */
aff1e150e13980354cfd794c74dac76a791a641eTimo Sirainen point_r->type = p_strdup(pool, getvfsbynumber(buf.f_type));
aff1e150e13980354cfd794c74dac76a791a641eTimo Sirainen#else
ad3a1b8f8e2a5596afb1b099a69ae6f688887eecTimo Sirainen point_r->type = p_strdup(pool, buf.f_fstypename);
aff1e150e13980354cfd794c74dac76a791a641eTimo Sirainen#endif
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen point_r->block_size = buf.f_bsize;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen return 1;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#else
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen /* Linux, Solaris: /etc/mtab reading */
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#ifdef HAVE_SYS_MNTTAB_H
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen union {
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen struct mnttab ent;
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen struct extmnttab ext;
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen } ent;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#else
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen struct mntent *ent;
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen struct stat st2;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#endif
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen struct stat st;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen const char *device_path = NULL, *mount_path = NULL, *type = NULL;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen unsigned int block_size;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen FILE *f;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen
e6bdf53eb0143af99e3eb977ff0f8a496ecd1a8dTimo Sirainen memset(point_r, 0, sizeof(*point_r));
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen if (stat(path, &st) < 0) {
9446c7a5d400cba60d097c528bd08312552438e3Timo Sirainen if (errno == ENOENT)
9446c7a5d400cba60d097c528bd08312552438e3Timo Sirainen return 0;
9446c7a5d400cba60d097c528bd08312552438e3Timo Sirainen
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen i_error("stat(%s) failed: %m", path);
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen return -1;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen }
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen block_size = st.st_blksize;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen#ifdef HAVE_SYS_VMOUNT_H
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen{
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen char static_mtab[STATIC_MTAB_SIZE], *mtab = static_mtab;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen int i, count;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen const struct vmount *vmt;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen count = mntctl(MCTL_QUERY, sizeof(static_mtab), mtab);
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen while (count == 0) {
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen unsigned int size = *(unsigned int *)mtab;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen mtab = t_malloc(size);
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen count = mntctl(MCTL_QUERY, size, mtab);
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen }
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen if (count < 0) {
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen i_error("mntctl(MCTL_QUERY) failed: %m");
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen return -1;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen }
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen vmt = (struct vmount *)mtab;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen for (i = 0; i < count && device_path == NULL; i++) {
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen struct stat vst;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen const char *vmt_base = (const char *)vmt;
310ef47cf7b913dcae4f7e66e931648dd7435ebdTimo Sirainen const char *vmt_object, *vmt_stub, *vmt_hostname;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen vmt_hostname = vmt_base + vmt->vmt_data[VMT_HOSTNAME].vmt_off;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen vmt_object = vmt_base + vmt->vmt_data[VMT_OBJECT].vmt_off;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen vmt_stub = vmt_base + vmt->vmt_data[VMT_STUB].vmt_off;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen switch (vmt->vmt_gfstype) {
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen case MNT_NFS:
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen case MNT_NFS3:
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen case MNT_NFS4:
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen case MNT_RFS4:
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen if (stat(vmt_stub, &vst) == 0 &&
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen st.st_dev == vst.st_dev) {
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen device_path = t_strconcat(vmt_hostname, ":",
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen vmt_object, NULL);
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen mount_path = vmt_stub;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen type = MNTTYPE_NFS;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen }
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen break;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen case MNT_J2:
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen case MNT_JFS:
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen if (stat(vmt_stub, &vst) == 0 &&
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen st.st_dev == vst.st_dev) {
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen device_path = vmt_object;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen mount_path = vmt_stub;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen type = MNTTYPE_JFS;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen }
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen break;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen }
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen vmt = CONST_PTR_OFFSET(vmt, vmt->vmt_length);
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen }
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen}
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen#elif defined(HAVE_SYS_MNTTAB_H)
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen /* Solaris */
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen f = fopen(MTAB_PATH, "r");
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen if (f == NULL) {
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen i_error("fopen(%s) failed: %m", MTAB_PATH);
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen return -1;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen }
221ff08d3c6895d6bccdac0281c5d357256dfbe3Timo Sirainen resetmnttab(f);
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen while ((getextmntent(f, &ent.ext, sizeof(ent.ext))) == 0) {
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen if (hasmntopt(&ent.ent, MNTOPT_IGNORE) != NULL)
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen continue;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen /* mnt_type contains tmpfs with swap */
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen if (strcmp(ent.ent.mnt_special, MNTTYPE_SWAP) == 0)
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen continue;
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen if (major(st.st_dev) == ent.ext.mnt_major &&
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen minor(st.st_dev) == ent.ext.mnt_minor) {
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen device_path = ent.ent.mnt_special;
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen mount_path = ent.ent.mnt_mountp;
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen type = ent.ent.mnt_fstype;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen break;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen }
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen }
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen fclose(f);
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#else
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen /* Linux */
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen f = setmntent(MTAB_PATH, "r");
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen if (f == NULL) {
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen i_error("setmntent(%s) failed: %m", MTAB_PATH);
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen return -1;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen }
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen while ((ent = getmntent(f)) != NULL) {
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen if (strcmp(ent->mnt_type, MNTTYPE_SWAP) == 0 ||
26bedc1f8aace67ff8f19f625a403a097017845aTimo Sirainen strcmp(ent->mnt_type, MNTTYPE_IGNORE) == 0 ||
26bedc1f8aace67ff8f19f625a403a097017845aTimo Sirainen strcmp(ent->mnt_type, MNTTYPE_ROOTFS) == 0)
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen continue;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen if (stat(ent->mnt_dir, &st2) == 0 &&
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen CMP_DEV_T(st.st_dev, st2.st_dev)) {
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen device_path = ent->mnt_fsname;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen mount_path = ent->mnt_dir;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen type = ent->mnt_type;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen break;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen }
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen }
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen endmntent(f);
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#endif
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen if (device_path == NULL)
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen return 0;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen point_r->device_path = p_strdup(pool, device_path);
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen point_r->mount_path = p_strdup(pool, mount_path);
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen point_r->type = p_strdup(pool, type);
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen point_r->block_size = block_size;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen return 1;
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen#endif
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen}