19733a808SDag-Erling Smørgrav /*- 2d63027b6SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 3d63027b6SPedro F. Giffuni * 49a14aa01SUlrich Spörlein * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav 59733a808SDag-Erling Smørgrav * All rights reserved. 69733a808SDag-Erling Smørgrav * 79733a808SDag-Erling Smørgrav * Redistribution and use in source and binary forms, with or without 89733a808SDag-Erling Smørgrav * modification, are permitted provided that the following conditions 99733a808SDag-Erling Smørgrav * are met: 109733a808SDag-Erling Smørgrav * 1. Redistributions of source code must retain the above copyright 119733a808SDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer 129733a808SDag-Erling Smørgrav * in this position and unchanged. 139733a808SDag-Erling Smørgrav * 2. Redistributions in binary form must reproduce the above copyright 149733a808SDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer in the 159733a808SDag-Erling Smørgrav * documentation and/or other materials provided with the distribution. 169733a808SDag-Erling Smørgrav * 3. The name of the author may not be used to endorse or promote products 179733a808SDag-Erling Smørgrav * derived from this software without specific prior written permission. 189733a808SDag-Erling Smørgrav * 199733a808SDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 209733a808SDag-Erling Smørgrav * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 219733a808SDag-Erling Smørgrav * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 229733a808SDag-Erling Smørgrav * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 239733a808SDag-Erling Smørgrav * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 249733a808SDag-Erling Smørgrav * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 259733a808SDag-Erling Smørgrav * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 269733a808SDag-Erling Smørgrav * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 279733a808SDag-Erling Smørgrav * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 289733a808SDag-Erling Smørgrav * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 299733a808SDag-Erling Smørgrav * 309733a808SDag-Erling Smørgrav * $FreeBSD$ 319733a808SDag-Erling Smørgrav */ 329733a808SDag-Erling Smørgrav 339733a808SDag-Erling Smørgrav #ifndef _PSEUDOFS_H_INCLUDED 349733a808SDag-Erling Smørgrav #define _PSEUDOFS_H_INCLUDED 359733a808SDag-Erling Smørgrav 3641c0675eSMartin Matuska #include <sys/jail.h> 3741c0675eSMartin Matuska 389733a808SDag-Erling Smørgrav /* 3941aa8697SDag-Erling Smørgrav * Opaque structures 4041aa8697SDag-Erling Smørgrav */ 41c9ad8a67SKelly Yancey struct mntarg; 4241aa8697SDag-Erling Smørgrav struct mount; 4341aa8697SDag-Erling Smørgrav struct nameidata; 4441aa8697SDag-Erling Smørgrav struct proc; 4541aa8697SDag-Erling Smørgrav struct sbuf; 4641aa8697SDag-Erling Smørgrav struct statfs; 4741aa8697SDag-Erling Smørgrav struct thread; 4841aa8697SDag-Erling Smørgrav struct uio; 4941aa8697SDag-Erling Smørgrav struct vfsconf; 5041aa8697SDag-Erling Smørgrav struct vnode; 5141aa8697SDag-Erling Smørgrav 5241aa8697SDag-Erling Smørgrav /* 539733a808SDag-Erling Smørgrav * Limits and constants 549733a808SDag-Erling Smørgrav */ 5581167243SMatt Macy #define PFS_NAMELEN 128 5641aa8697SDag-Erling Smørgrav #define PFS_FSNAMELEN 16 /* equal to MFSNAMELEN */ 570a7c8d30SDmitry Chagin #define PFS_DELEN (offsetof(struct dirent, d_name) + PFS_NAMELEN) 589733a808SDag-Erling Smørgrav 599733a808SDag-Erling Smørgrav typedef enum { 609733a808SDag-Erling Smørgrav pfstype_none = 0, 619733a808SDag-Erling Smørgrav pfstype_root, 629733a808SDag-Erling Smørgrav pfstype_dir, 639733a808SDag-Erling Smørgrav pfstype_this, 649733a808SDag-Erling Smørgrav pfstype_parent, 659733a808SDag-Erling Smørgrav pfstype_file, 669733a808SDag-Erling Smørgrav pfstype_symlink, 67649ad985SDag-Erling Smørgrav pfstype_procdir 689733a808SDag-Erling Smørgrav } pfs_type_t; 699733a808SDag-Erling Smørgrav 709733a808SDag-Erling Smørgrav /* 718712e867SDag-Erling Smørgrav * Flags 728712e867SDag-Erling Smørgrav */ 737d8f809fSDag-Erling Smørgrav #define PFS_RD 0x0001 /* readable */ 747d8f809fSDag-Erling Smørgrav #define PFS_WR 0x0002 /* writeable */ 757d8f809fSDag-Erling Smørgrav #define PFS_RDWR (PFS_RD|PFS_WR) 768712e867SDag-Erling Smørgrav #define PFS_RAWRD 0x0004 /* raw reader */ 778712e867SDag-Erling Smørgrav #define PFS_RAWWR 0x0008 /* raw writer */ 787d8f809fSDag-Erling Smørgrav #define PFS_RAW (PFS_RAWRD|PFS_RAWWR) 7933802b9eSDag-Erling Smørgrav #define PFS_PROCDEP 0x0010 /* process-dependent */ 8081167243SMatt Macy #define PFS_NOWAIT 0x0020 /* allow malloc to fail */ 8120172854SConrad Meyer #define PFS_AUTODRAIN 0x0040 /* sbuf_print can sleep to drain */ 828712e867SDag-Erling Smørgrav 838712e867SDag-Erling Smørgrav /* 849733a808SDag-Erling Smørgrav * Data structures 859733a808SDag-Erling Smørgrav */ 869733a808SDag-Erling Smørgrav struct pfs_info; 879733a808SDag-Erling Smørgrav struct pfs_node; 889733a808SDag-Erling Smørgrav 8980a3cef8SDag-Erling Smørgrav /* 9033802b9eSDag-Erling Smørgrav * Init / uninit callback 9133802b9eSDag-Erling Smørgrav */ 9233802b9eSDag-Erling Smørgrav #define PFS_INIT_ARGS \ 9333802b9eSDag-Erling Smørgrav struct pfs_info *pi, struct vfsconf *vfc 94388596dfSDag-Erling Smørgrav #define PFS_INIT_ARGNAMES \ 95388596dfSDag-Erling Smørgrav pi, vfc 9633802b9eSDag-Erling Smørgrav #define PFS_INIT_PROTO(name) \ 9733802b9eSDag-Erling Smørgrav int name(PFS_INIT_ARGS); 9833802b9eSDag-Erling Smørgrav typedef int (*pfs_init_t)(PFS_INIT_ARGS); 9933802b9eSDag-Erling Smørgrav 10033802b9eSDag-Erling Smørgrav /* 10180a3cef8SDag-Erling Smørgrav * Filler callback 102388596dfSDag-Erling Smørgrav * Called with proc held but unlocked 10380a3cef8SDag-Erling Smørgrav */ 104649ad985SDag-Erling Smørgrav #define PFS_FILL_ARGS \ 1058712e867SDag-Erling Smørgrav struct thread *td, struct proc *p, struct pfs_node *pn, \ 1068712e867SDag-Erling Smørgrav struct sbuf *sb, struct uio *uio 107388596dfSDag-Erling Smørgrav #define PFS_FILL_ARGNAMES \ 108388596dfSDag-Erling Smørgrav td, p, pn, sb, uio 109649ad985SDag-Erling Smørgrav #define PFS_FILL_PROTO(name) \ 110649ad985SDag-Erling Smørgrav int name(PFS_FILL_ARGS); 111649ad985SDag-Erling Smørgrav typedef int (*pfs_fill_t)(PFS_FILL_ARGS); 1129733a808SDag-Erling Smørgrav 11380a3cef8SDag-Erling Smørgrav /* 11480a3cef8SDag-Erling Smørgrav * Attribute callback 115388596dfSDag-Erling Smørgrav * Called with proc locked 11680a3cef8SDag-Erling Smørgrav */ 11780a3cef8SDag-Erling Smørgrav struct vattr; 11880a3cef8SDag-Erling Smørgrav #define PFS_ATTR_ARGS \ 11980a3cef8SDag-Erling Smørgrav struct thread *td, struct proc *p, struct pfs_node *pn, \ 12080a3cef8SDag-Erling Smørgrav struct vattr *vap 121388596dfSDag-Erling Smørgrav #define PFS_ATTR_ARGNAMES \ 122388596dfSDag-Erling Smørgrav td, p, pn, vap 12380a3cef8SDag-Erling Smørgrav #define PFS_ATTR_PROTO(name) \ 12480a3cef8SDag-Erling Smørgrav int name(PFS_ATTR_ARGS); 12580a3cef8SDag-Erling Smørgrav typedef int (*pfs_attr_t)(PFS_ATTR_ARGS); 12680a3cef8SDag-Erling Smørgrav 1279733a808SDag-Erling Smørgrav /* 128198bc14bSDag-Erling Smørgrav * Visibility callback 129388596dfSDag-Erling Smørgrav * Called with proc locked 130198bc14bSDag-Erling Smørgrav */ 131198bc14bSDag-Erling Smørgrav #define PFS_VIS_ARGS \ 132198bc14bSDag-Erling Smørgrav struct thread *td, struct proc *p, struct pfs_node *pn 133388596dfSDag-Erling Smørgrav #define PFS_VIS_ARGNAMES \ 134388596dfSDag-Erling Smørgrav td, p, pn 135198bc14bSDag-Erling Smørgrav #define PFS_VIS_PROTO(name) \ 136198bc14bSDag-Erling Smørgrav int name(PFS_VIS_ARGS); 137198bc14bSDag-Erling Smørgrav typedef int (*pfs_vis_t)(PFS_VIS_ARGS); 138198bc14bSDag-Erling Smørgrav 139198bc14bSDag-Erling Smørgrav /* 14018319000SDag-Erling Smørgrav * Ioctl callback 141388596dfSDag-Erling Smørgrav * Called with proc locked 14218319000SDag-Erling Smørgrav */ 14318319000SDag-Erling Smørgrav #define PFS_IOCTL_ARGS \ 14418319000SDag-Erling Smørgrav struct thread *td, struct proc *p, struct pfs_node *pn, \ 1457b726be3SDag-Erling Smørgrav unsigned long cmd, void *data 146388596dfSDag-Erling Smørgrav #define PFS_IOCTL_ARGNAMES \ 147388596dfSDag-Erling Smørgrav td, p, pn, cmd, data 14818319000SDag-Erling Smørgrav #define PFS_IOCTL_PROTO(name) \ 14918319000SDag-Erling Smørgrav int name(PFS_IOCTL_ARGS); 15018319000SDag-Erling Smørgrav typedef int (*pfs_ioctl_t)(PFS_IOCTL_ARGS); 15118319000SDag-Erling Smørgrav 15218319000SDag-Erling Smørgrav /* 15341a35633SBrian Feldman * Getextattr callback 154388596dfSDag-Erling Smørgrav * Called with proc locked 15541a35633SBrian Feldman */ 15641a35633SBrian Feldman #define PFS_GETEXTATTR_ARGS \ 15741a35633SBrian Feldman struct thread *td, struct proc *p, struct pfs_node *pn, \ 15841a35633SBrian Feldman int attrnamespace, const char *name, struct uio *uio, \ 15974237f55SRobert Watson size_t *size, struct ucred *cred 160388596dfSDag-Erling Smørgrav #define PFS_GETEXTATTR_ARGNAMES \ 161388596dfSDag-Erling Smørgrav td, p, pn, attrnamespace, name, uio, size, cred 16241a35633SBrian Feldman #define PFS_GETEXTATTR_PROTO(name) \ 16341a35633SBrian Feldman int name(PFS_GETEXTATTR_ARGS); 16441a35633SBrian Feldman struct ucred; 16541a35633SBrian Feldman typedef int (*pfs_getextattr_t)(PFS_GETEXTATTR_ARGS); 16641a35633SBrian Feldman 16741a35633SBrian Feldman /* 16898c7e22cSDag-Erling Smørgrav * Last-close callback 169388596dfSDag-Erling Smørgrav * Called with proc locked 17098c7e22cSDag-Erling Smørgrav */ 17198c7e22cSDag-Erling Smørgrav #define PFS_CLOSE_ARGS \ 17298c7e22cSDag-Erling Smørgrav struct thread *td, struct proc *p, struct pfs_node *pn 173388596dfSDag-Erling Smørgrav #define PFS_CLOSE_ARGNAMES \ 174388596dfSDag-Erling Smørgrav td, p, pn 17598c7e22cSDag-Erling Smørgrav #define PFS_CLOSE_PROTO(name) \ 17698c7e22cSDag-Erling Smørgrav int name(PFS_CLOSE_ARGS); 17798c7e22cSDag-Erling Smørgrav typedef int (*pfs_close_t)(PFS_CLOSE_ARGS); 17898c7e22cSDag-Erling Smørgrav 17998c7e22cSDag-Erling Smørgrav /* 180771709ebSDag-Erling Smørgrav * Destroy callback 181771709ebSDag-Erling Smørgrav */ 182771709ebSDag-Erling Smørgrav #define PFS_DESTROY_ARGS \ 183771709ebSDag-Erling Smørgrav struct pfs_node *pn 184388596dfSDag-Erling Smørgrav #define PFS_DESTROY_ARGNAMES \ 185388596dfSDag-Erling Smørgrav pn 186771709ebSDag-Erling Smørgrav #define PFS_DESTROY_PROTO(name) \ 187771709ebSDag-Erling Smørgrav int name(PFS_DESTROY_ARGS); 188771709ebSDag-Erling Smørgrav typedef int (*pfs_destroy_t)(PFS_DESTROY_ARGS); 189771709ebSDag-Erling Smørgrav 190771709ebSDag-Erling Smørgrav /* 1919733a808SDag-Erling Smørgrav * pfs_info: describes a pseudofs instance 192f61bc4eaSDag-Erling Smørgrav * 19329ffb32cSKonstantin Belousov * The pi_mutex is only used to avoid using the global subr_unit lock 19429ffb32cSKonstantin Belousov * for unrhdr. The rest of struct pfs_info is only modified during 19529ffb32cSKonstantin Belousov * vfs_init() and vfs_uninit() of the consumer filesystem. 1969733a808SDag-Erling Smørgrav */ 1979733a808SDag-Erling Smørgrav struct pfs_info { 19841aa8697SDag-Erling Smørgrav char pi_name[PFS_FSNAMELEN]; 19933802b9eSDag-Erling Smørgrav pfs_init_t pi_init; 20033802b9eSDag-Erling Smørgrav pfs_init_t pi_uninit; 201f61bc4eaSDag-Erling Smørgrav 202f61bc4eaSDag-Erling Smørgrav /* members below this line are initialized at run time */ 20333802b9eSDag-Erling Smørgrav struct pfs_node *pi_root; 2049733a808SDag-Erling Smørgrav struct mtx pi_mutex; 2057f661c6bSPoul-Henning Kamp struct unrhdr *pi_unrhdr; 2069733a808SDag-Erling Smørgrav }; 2079733a808SDag-Erling Smørgrav 2089733a808SDag-Erling Smørgrav /* 2099733a808SDag-Erling Smørgrav * pfs_node: describes a node (file or directory) within a pseudofs 210388596dfSDag-Erling Smørgrav * 211388596dfSDag-Erling Smørgrav * - Fields marked (o) are protected by the node's own mutex. 212388596dfSDag-Erling Smørgrav * - Fields marked (p) are protected by the node's parent's mutex. 213388596dfSDag-Erling Smørgrav * - Remaining fields are not protected by any lock and are assumed to be 214388596dfSDag-Erling Smørgrav * immutable once the node has been created. 215388596dfSDag-Erling Smørgrav * 216388596dfSDag-Erling Smørgrav * To prevent deadlocks, if a node's mutex is to be held at the same time 217388596dfSDag-Erling Smørgrav * as its parent's (e.g. when adding or removing nodes to a directory), 218388596dfSDag-Erling Smørgrav * the parent's mutex must always be acquired first. Unfortunately, this 219388596dfSDag-Erling Smørgrav * is not enforcable by WITNESS. 2209733a808SDag-Erling Smørgrav */ 2219733a808SDag-Erling Smørgrav struct pfs_node { 2229733a808SDag-Erling Smørgrav pfs_type_t pn_type; 223388596dfSDag-Erling Smørgrav int pn_flags; 224388596dfSDag-Erling Smørgrav struct mtx pn_mutex; 225388596dfSDag-Erling Smørgrav void *pn_data; /* (o) */ 226388596dfSDag-Erling Smørgrav 227388596dfSDag-Erling Smørgrav pfs_fill_t pn_fill; 22818319000SDag-Erling Smørgrav pfs_ioctl_t pn_ioctl; 22998c7e22cSDag-Erling Smørgrav pfs_close_t pn_close; 23080a3cef8SDag-Erling Smørgrav pfs_attr_t pn_attr; 231198bc14bSDag-Erling Smørgrav pfs_vis_t pn_vis; 23241a35633SBrian Feldman pfs_getextattr_t pn_getextattr; 233771709ebSDag-Erling Smørgrav pfs_destroy_t pn_destroy; 23433802b9eSDag-Erling Smørgrav 23533802b9eSDag-Erling Smørgrav struct pfs_info *pn_info; 236388596dfSDag-Erling Smørgrav u_int32_t pn_fileno; /* (o) */ 237388596dfSDag-Erling Smørgrav 238388596dfSDag-Erling Smørgrav struct pfs_node *pn_parent; /* (o) */ 239388596dfSDag-Erling Smørgrav struct pfs_node *pn_nodes; /* (o) */ 240cf389852SEdward Tomasz Napierala struct pfs_node *pn_last_node; /* (o) */ 241388596dfSDag-Erling Smørgrav struct pfs_node *pn_next; /* (p) */ 242*7f723243SDmitry Chagin char pn_name[]; /* Keep it last */ 2439733a808SDag-Erling Smørgrav }; 2449733a808SDag-Erling Smørgrav 2459733a808SDag-Erling Smørgrav /* 2469733a808SDag-Erling Smørgrav * VFS interface 2479733a808SDag-Erling Smørgrav */ 248dfd233edSAttilio Rao int pfs_mount (struct pfs_info *pi, struct mount *mp); 249cc672d35SKirk McKusick int pfs_cmount (struct mntarg *ma, void *data, uint64_t flags); 250dfd233edSAttilio Rao int pfs_unmount (struct mount *mp, int mntflags); 251d9b2d9f7SJeff Roberson int pfs_root (struct mount *mp, int flags, 252dfd233edSAttilio Rao struct vnode **vpp); 253dfd233edSAttilio Rao int pfs_statfs (struct mount *mp, struct statfs *sbp); 2549733a808SDag-Erling Smørgrav int pfs_init (struct pfs_info *pi, struct vfsconf *vfc); 2559733a808SDag-Erling Smørgrav int pfs_uninit (struct pfs_info *pi, struct vfsconf *vfc); 2569733a808SDag-Erling Smørgrav 2579733a808SDag-Erling Smørgrav /* 25833802b9eSDag-Erling Smørgrav * Directory structure construction and manipulation 259b84ce334SDag-Erling Smørgrav */ 260b331ec01SDag-Erling Smørgrav struct pfs_node *pfs_create_dir (struct pfs_node *parent, const char *name, 261771709ebSDag-Erling Smørgrav pfs_attr_t attr, pfs_vis_t vis, 262771709ebSDag-Erling Smørgrav pfs_destroy_t destroy, int flags); 263b331ec01SDag-Erling Smørgrav struct pfs_node *pfs_create_file(struct pfs_node *parent, const char *name, 26433802b9eSDag-Erling Smørgrav pfs_fill_t fill, pfs_attr_t attr, 265771709ebSDag-Erling Smørgrav pfs_vis_t vis, pfs_destroy_t destroy, 266771709ebSDag-Erling Smørgrav int flags); 267b331ec01SDag-Erling Smørgrav struct pfs_node *pfs_create_link(struct pfs_node *parent, const char *name, 26833802b9eSDag-Erling Smørgrav pfs_fill_t fill, pfs_attr_t attr, 269771709ebSDag-Erling Smørgrav pfs_vis_t vis, pfs_destroy_t destroy, 270771709ebSDag-Erling Smørgrav int flags); 271b331ec01SDag-Erling Smørgrav struct pfs_node *pfs_find_node (struct pfs_node *parent, const char *name); 27215bad11fSDag-Erling Smørgrav void pfs_purge (struct pfs_node *pn); 27333802b9eSDag-Erling Smørgrav int pfs_destroy (struct pfs_node *pn); 274b84ce334SDag-Erling Smørgrav 275b84ce334SDag-Erling Smørgrav /* 2769733a808SDag-Erling Smørgrav * Now for some initialization magic... 2779733a808SDag-Erling Smørgrav */ 2780e5c6bd4SJamie Gritton #define PSEUDOFS(name, version, flags) \ 2799733a808SDag-Erling Smørgrav \ 2809733a808SDag-Erling Smørgrav static struct pfs_info name##_info = { \ 2819733a808SDag-Erling Smørgrav #name, \ 282ce2fb577SPoul-Henning Kamp name##_init, \ 283ce2fb577SPoul-Henning Kamp name##_uninit, \ 2849733a808SDag-Erling Smørgrav }; \ 2859733a808SDag-Erling Smørgrav \ 2869733a808SDag-Erling Smørgrav static int \ 287dfd233edSAttilio Rao _##name##_mount(struct mount *mp) { \ 2886828ba63SKonstantin Belousov return (pfs_mount(&name##_info, mp)); \ 2899733a808SDag-Erling Smørgrav } \ 2909733a808SDag-Erling Smørgrav \ 2919733a808SDag-Erling Smørgrav static int \ 2929733a808SDag-Erling Smørgrav _##name##_init(struct vfsconf *vfc) { \ 2936828ba63SKonstantin Belousov return (pfs_init(&name##_info, vfc)); \ 2949733a808SDag-Erling Smørgrav } \ 2959733a808SDag-Erling Smørgrav \ 2969733a808SDag-Erling Smørgrav static int \ 2979733a808SDag-Erling Smørgrav _##name##_uninit(struct vfsconf *vfc) { \ 2986828ba63SKonstantin Belousov return (pfs_uninit(&name##_info, vfc)); \ 2999733a808SDag-Erling Smørgrav } \ 3009733a808SDag-Erling Smørgrav \ 3017005ce8aSDag-Erling Smørgrav static struct vfsops name##_vfsops = { \ 302c9ad8a67SKelly Yancey .vfs_cmount = pfs_cmount, \ 3037652131bSPoul-Henning Kamp .vfs_init = _##name##_init, \ 3045e8c582aSPoul-Henning Kamp .vfs_mount = _##name##_mount, \ 3057652131bSPoul-Henning Kamp .vfs_root = pfs_root, \ 3067652131bSPoul-Henning Kamp .vfs_statfs = pfs_statfs, \ 3077652131bSPoul-Henning Kamp .vfs_uninit = _##name##_uninit, \ 3087652131bSPoul-Henning Kamp .vfs_unmount = pfs_unmount, \ 3099733a808SDag-Erling Smørgrav }; \ 3100e5c6bd4SJamie Gritton VFS_SET(name##_vfsops, name, VFCF_SYNTHETIC | flags); \ 3118712e867SDag-Erling Smørgrav MODULE_VERSION(name, version); \ 31298c7e22cSDag-Erling Smørgrav MODULE_DEPEND(name, pseudofs, 1, 1, 1); 3139733a808SDag-Erling Smørgrav 3149733a808SDag-Erling Smørgrav #endif 315