1 /******************************************************************************
2  *
3  * Project:  Hierarchical Data Format Release 5 (HDF5)
4  * Authors:  Denis Nadeau <denis.nadeau@gmail.com>
5  *           Sam Gillingham <gillingham.sam@gmail.com>
6  *
7  ******************************************************************************
8  * Copyright (c) 2008-2018, Even Rouault <even.rouault at spatialys.com>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29  // This file contains the Virtual File Layer implementation that calls through
30  // to the VSI functions and should be included by HDF5 based drivers that wish
31  // to use the VFL for /vsi file system support.
32 
33 #ifndef HDF5VFL_H_INCLUDED_
34 #define HDF5VFL_H_INCLUDED_
35 
36 #include "cpl_port.h"
37 
38 #include <algorithm>
39 #include <mutex>
40 
41 extern "C" int CPL_DLL GDALIsInGlobalDestructor(void);
42 
43 #ifdef H5FD_FEAT_SUPPORTS_SWMR_IO
44 #define HDF5_1_10_OR_LATER
45 #endif
46 
47 static std::mutex gMutex;
48 static hid_t hFileDriver = -1;
49 
50 static H5FD_t *HDF5_vsil_open(const char *name, unsigned flags, hid_t fapl_id,
51             haddr_t maxaddr);
52 static herr_t HDF5_vsil_close(H5FD_t *_file);
53 static herr_t HDF5_vsil_query(const H5FD_t *_f1, unsigned long *flags);
54 static haddr_t HDF5_vsil_get_eoa(const H5FD_t *_file, H5FD_mem_t type);
55 static herr_t HDF5_vsil_set_eoa(H5FD_t *_file, H5FD_mem_t type, haddr_t addr);
56 static haddr_t HDF5_vsil_get_eof(const H5FD_t *_file
57 #ifdef HDF5_1_10_OR_LATER
58                                  , H5FD_mem_t type
59 #endif
60 );
61 static herr_t HDF5_vsil_read(H5FD_t *_file, H5FD_mem_t type, hid_t fapl_id,
62                              haddr_t addr, size_t size, void *buf);
63 static herr_t HDF5_vsil_write(H5FD_t *_file, H5FD_mem_t type, hid_t fapl_id,
64                               haddr_t addr, size_t size, const void *buf);
65 static herr_t HDF5_vsil_truncate(H5FD_t *_file, hid_t dxpl_id, hbool_t closing);
66 
67 static hid_t HDF5VFLGetFileDriver();
68 static void HDF5VFLUnloadFileDriver();
69 
70 #define MAXADDR (((haddr_t)1<<(8*sizeof(haddr_t)-1))-1)
71 
72 /* See https://support.hdfgroup.org/HDF5/doc/TechNotes/VFL.html */
73 static const H5FD_class_t HDF5_vsil_g = {
74     "vsil",                     /* name */
75     MAXADDR,                    /* maxaddr  */
76     H5F_CLOSE_WEAK,             /* fc_degree  */
77 #ifdef HDF5_1_10_OR_LATER
78     nullptr,                    /* terminate */
79 #endif
80     nullptr,                    /* sb_size  */
81     nullptr,                    /* sb_encode */
82     nullptr,                    /* sb_decode */
83     0,                          /* fapl_size */
84     nullptr,                    /* fapl_get  */
85     nullptr,                    /* fapl_copy */
86     nullptr,                    /* fapl_free */
87     0,                          /* dxpl_size */
88     nullptr,                    /* dxpl_copy */
89     nullptr,                    /* dxpl_free */
90     HDF5_vsil_open,             /* open */
91     HDF5_vsil_close,            /* close */
92     nullptr,                    /* cmp  */
93     HDF5_vsil_query,            /* query */
94     nullptr,                    /* get_type_map */
95     nullptr,                    /* alloc */
96     nullptr,                    /* free */
97     HDF5_vsil_get_eoa,          /* get_eoa */
98     HDF5_vsil_set_eoa,          /* set_eoa */
99     HDF5_vsil_get_eof,          /* get_eof */
100     nullptr,                    /* get_handle */
101     HDF5_vsil_read,             /* read */
102     HDF5_vsil_write,            /* write */
103     nullptr,                    /* flush */
104     HDF5_vsil_truncate,         /* truncate */
105     nullptr,                    /* lock */
106     nullptr,                    /* unlock */
107     H5FD_FLMAP_DICHOTOMY        /* fl_map */
108 };
109 
110 typedef struct HDF5_vsil_t {
111     H5FD_t          pub;            /* must be first */
112     VSILFILE       *fp = nullptr;
113     haddr_t         eoa = 0;
114     haddr_t         eof = 0;
115 } HDF5_vsil_t;
116 
HDF5_vsil_open(const char * name,unsigned flags,hid_t,haddr_t)117 static H5FD_t *HDF5_vsil_open(const char *name, unsigned flags,
118                               hid_t /*fapl_id*/, haddr_t /*maxaddr*/)
119 {
120     const char* openFlags = "rb";
121     if( (H5F_ACC_RDWR & flags) )
122         openFlags = "rb+";
123     if( (H5F_ACC_TRUNC & flags) || (H5F_ACC_CREAT & flags) )
124         openFlags = "wb+";
125 
126     VSILFILE* fp = VSIFOpenL(name, openFlags);
127     if( !fp )
128     {
129         return nullptr;
130     }
131     if( (H5F_ACC_TRUNC & flags) )
132     {
133         VSIFTruncateL(fp, 0);
134     }
135 
136     HDF5_vsil_t* fh = new HDF5_vsil_t;
137     memset(&fh->pub, 0, sizeof(fh->pub));
138     if( !fh )
139     {
140         VSIFCloseL(fp);
141         return nullptr;
142     }
143     fh->fp = fp;
144 
145     VSIFSeekL(fh->fp, 0, SEEK_END);
146     fh->eof = static_cast<haddr_t>(VSIFTellL(fh->fp));
147 
148     return reinterpret_cast<H5FD_t*>(fh);
149 }
150 
HDF5_vsil_close(H5FD_t * _file)151 static herr_t HDF5_vsil_close(H5FD_t *_file)
152 {
153     HDF5_vsil_t* fh = reinterpret_cast<HDF5_vsil_t*>(_file);
154     int ret = VSIFCloseL(fh->fp);
155     delete fh;
156     return ret;
157 }
158 
HDF5_vsil_query(const H5FD_t *,unsigned long * flags)159 static herr_t HDF5_vsil_query(const H5FD_t *, unsigned long *flags /* out */)
160 {
161     *flags = H5FD_FEAT_AGGREGATE_METADATA |
162              H5FD_FEAT_ACCUMULATE_METADATA |
163              H5FD_FEAT_DATA_SIEVE |
164              H5FD_FEAT_AGGREGATE_SMALLDATA;
165     return 0;
166 }
167 
HDF5_vsil_get_eoa(const H5FD_t * _file,H5FD_mem_t)168 static haddr_t HDF5_vsil_get_eoa(const H5FD_t *_file, H5FD_mem_t /*type*/)
169 {
170     const HDF5_vsil_t* fh = reinterpret_cast<const HDF5_vsil_t*>(_file);
171     return fh->eoa;
172 }
173 
HDF5_vsil_set_eoa(H5FD_t * _file,H5FD_mem_t,haddr_t addr)174 static herr_t HDF5_vsil_set_eoa(H5FD_t *_file, H5FD_mem_t /*type*/,
175                                 haddr_t addr)
176 {
177     HDF5_vsil_t* fh = reinterpret_cast<HDF5_vsil_t*>(_file);
178     fh->eoa = addr;
179     return 0;
180 }
181 
HDF5_vsil_get_eof(const H5FD_t * _file,H5FD_mem_t)182 static haddr_t HDF5_vsil_get_eof(const H5FD_t *_file
183 #ifdef HDF5_1_10_OR_LATER
184                                  , H5FD_mem_t /* type */
185 #endif
186                                 )
187 {
188     const HDF5_vsil_t* fh = reinterpret_cast<const HDF5_vsil_t*>(_file);
189     return fh->eof;
190 }
191 
HDF5_vsil_read(H5FD_t * _file,H5FD_mem_t,hid_t,haddr_t addr,size_t size,void * buf)192 static herr_t HDF5_vsil_read(H5FD_t *_file, H5FD_mem_t /* type */,
193                              hid_t /* dxpl_id */,
194                              haddr_t addr, size_t size, void *buf /*out*/)
195 {
196     HDF5_vsil_t* fh = reinterpret_cast<HDF5_vsil_t*>(_file);
197     VSIFSeekL(fh->fp, static_cast<vsi_l_offset>(addr), SEEK_SET);
198     return VSIFReadL(buf, size, 1, fh->fp) == 1 ? 0 : -1;
199 }
200 
HDF5_vsil_write(H5FD_t * _file,H5FD_mem_t,hid_t,haddr_t addr,size_t size,const void * buf)201 static herr_t HDF5_vsil_write(H5FD_t *_file, H5FD_mem_t /* type */,
202                               hid_t /* dxpl_id */,
203                               haddr_t addr, size_t size,
204                               const void *buf /*out*/)
205 {
206     HDF5_vsil_t* fh = reinterpret_cast<HDF5_vsil_t*>(_file);
207     VSIFSeekL(fh->fp, static_cast<vsi_l_offset>(addr), SEEK_SET);
208     int ret = VSIFWriteL(buf, size, 1, fh->fp) == 1 ? 0 : -1;
209     fh->eof = std::max(fh->eof, static_cast<haddr_t>(VSIFTellL(fh->fp)));
210     return ret;
211 }
212 
HDF5_vsil_truncate(H5FD_t * _file,hid_t,hbool_t)213 static herr_t HDF5_vsil_truncate(H5FD_t *_file, hid_t /* dxpl_id*/,
214                                  hbool_t /*closing*/)
215 {
216     HDF5_vsil_t* fh = reinterpret_cast<HDF5_vsil_t*>(_file);
217     if(fh->eoa != fh->eof)
218     {
219         if( VSIFTruncateL(fh->fp, fh->eoa) < 0 )
220         {
221             return -1;
222         }
223         fh->eof = fh->eoa;
224     }
225     return 0;
226 }
227 
228 /************************************************************************/
229 /*                       HDF5VFLGetFileDriver()                         */
230 /************************************************************************/
231 
HDF5VFLGetFileDriver()232 static hid_t HDF5VFLGetFileDriver()
233 {
234     std::lock_guard<std::mutex> oLock(gMutex);
235     if( hFileDriver < 0 )
236     {
237         hFileDriver = H5FDregister(&HDF5_vsil_g);
238 #if H5E_auto_t_vers == 2
239         // also, don't print error messages from KEA driver.
240         // (which uses H5E_auto_t_vers=2 - the default, hdf uses 1 for some reason).
241         // These tend to be meaningless - ie no GCP's found etc.
242         // They didn't seem to be shown when we didn't use the VFL layer
243         // - maybe VFL turns them on?
244         H5Eset_auto(H5E_DEFAULT, nullptr, nullptr);
245 #endif
246     }
247     return hFileDriver;
248 }
249 
250 /************************************************************************/
251 /*                     HDF5VFLUnloadFileDriver()                        */
252 /************************************************************************/
253 
HDF5VFLUnloadFileDriver()254 static void HDF5VFLUnloadFileDriver()
255 {
256     if( !GDALIsInGlobalDestructor() )
257     {
258         std::lock_guard<std::mutex> oLock(gMutex);
259         if( hFileDriver >= 0 )
260         {
261             H5FDunregister(hFileDriver);
262             hFileDriver = -1;
263         }
264     }
265 }
266 
267 #endif /* HDF5VFL_H_INCLUDED_ */
268