~ubuntu-branches/ubuntu/trusty/mapcache/trusty

« back to all changes in this revision

Viewing changes to lib/cache_disk.c

  • Committer: Package Import Robot
  • Author(s): Bas Couwenberg
  • Date: 2013-09-11 19:16:06 UTC
  • Revision ID: package-import@ubuntu.com-20130911191606-9aydo919w4dgjx9v
Tags: upstream-1.2.0
ImportĀ upstreamĀ versionĀ 1.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************************************************
 
2
 * $Id$
 
3
 *
 
4
 * Project:  MapServer
 
5
 * Purpose:  MapCache tile caching: filesytem cache backend.
 
6
 * Author:   Thomas Bonfort and the MapServer team.
 
7
 *
 
8
 ******************************************************************************
 
9
 * Copyright (c) 1996-2011 Regents of the University of Minnesota.
 
10
 *
 
11
 * Permission is hereby granted, free of charge, to any person obtaining a
 
12
 * copy of this software and associated documentation files (the "Software"),
 
13
 * to deal in the Software without restriction, including without limitation
 
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 
15
 * and/or sell copies of the Software, and to permit persons to whom the
 
16
 * Software is furnished to do so, subject to the following conditions:
 
17
 *
 
18
 * The above copyright notice and this permission notice shall be included in
 
19
 * all copies of this Software or works derived from this Software.
 
20
 *
 
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 
22
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 
27
 * DEALINGS IN THE SOFTWARE.
 
28
 *****************************************************************************/
 
29
 
 
30
#include "mapcache.h"
 
31
#include <apr_file_info.h>
 
32
#include <apr_strings.h>
 
33
#include <apr_file_io.h>
 
34
#include <string.h>
 
35
#include <errno.h>
 
36
#include <apr_mmap.h>
 
37
 
 
38
#ifdef HAVE_SYMLINK
 
39
#include <unistd.h>
 
40
#endif
 
41
 
 
42
/**
 
43
 * \brief computes the relative path between two destinations
 
44
 *
 
45
 * \param tilename the absolute filename of the tile
 
46
 * \param blankname the absolute path of the blank tile image
 
47
 */
 
48
 
 
49
char* relative_path(mapcache_context *ctx, char* tilename, char* blankname)
 
50
{
 
51
  int updir_cnt = 0;
 
52
  char *blank_rel = "";
 
53
 
 
54
  /* work up the directory paths of the tile and blank filename to find the common
 
55
   root */
 
56
  char *tile_it = tilename, *blank_it = blankname;
 
57
  if(*tile_it != *blank_it) {
 
58
    /* the two files have no common root.
 
59
     * This really shouldn't happen on a unix FS hierarchy, and symbolic linking
 
60
     * is enabled only on these platforms, so this case should in practice never
 
61
     * happen.
 
62
     * we return the absolute path, and should probably set a warning message
 
63
     */
 
64
    return apr_pstrdup(ctx->pool, blankname);
 
65
  }
 
66
  while(*(tile_it+1) && *(blank_it+1) && *(tile_it+1) == *(blank_it+1)) {
 
67
    tile_it++;
 
68
    blank_it++;
 
69
  }
 
70
 
 
71
  /* tile_it and blank_it point on the last common character of the two filenames,
 
72
   which should be a '/'. If not, return the full blank name
 
73
   * (and set a warning message? )*/
 
74
  if(*tile_it != *blank_it || *tile_it != '/') {
 
75
    return apr_pstrdup(ctx->pool, blankname);
 
76
  }
 
77
 
 
78
  blank_it++;
 
79
  while(*tile_it == '/') tile_it++; /*skip leading '/'s*/
 
80
 
 
81
  /* blank_it now contains the path that must be appended after the relative
 
82
   part of the constructed path,e.g.:
 
83
     - tilename = "/basepath/tilesetname/gridname/03/000/05/08.png"
 
84
     - blankname = "/basepath/tilesetname/gridname/blanks/005599FF.png"
 
85
   then
 
86
     - tile_it is "03/000/05/08.png"
 
87
     - blank_it is "blanks/005599FF.png"
 
88
   */
 
89
 
 
90
  /* we now count the number of '/' in the remaining tilename */
 
91
  while(*tile_it) {
 
92
    if(*tile_it == '/') {
 
93
      updir_cnt++;
 
94
      /* also skip consecutive '/'s */
 
95
      while(*(tile_it+1)=='/') tile_it++;
 
96
    }
 
97
    tile_it ++;
 
98
  }
 
99
 
 
100
  while(updir_cnt--) {
 
101
    blank_rel = apr_pstrcat(ctx->pool, blank_rel, "../", NULL);
 
102
  }
 
103
  blank_rel = apr_pstrcat(ctx->pool,blank_rel,blank_it,NULL);
 
104
  return blank_rel;
 
105
}
 
106
 
 
107
/**
 
108
 * \brief returns base path for given tile
 
109
 *
 
110
 * \param tile the tile to get base path from
 
111
 * \param path pointer to a char* that will contain the filename
 
112
 * \private \memberof mapcache_cache_disk
 
113
 */
 
114
static void _mapcache_cache_disk_base_tile_key(mapcache_context *ctx, mapcache_tile *tile, char **path)
 
115
{
 
116
  *path = apr_pstrcat(ctx->pool,
 
117
                      ((mapcache_cache_disk*)tile->tileset->cache)->base_directory,"/",
 
118
                      tile->tileset->name,"/",
 
119
                      tile->grid_link->grid->name,
 
120
                      NULL);
 
121
  if(tile->dimensions) {
 
122
    const apr_array_header_t *elts = apr_table_elts(tile->dimensions);
 
123
    int i = elts->nelts;
 
124
    while(i--) {
 
125
      apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,i,apr_table_entry_t));
 
126
      const char *dimval = mapcache_util_str_sanitize(ctx->pool,entry->val,"/.",'#');
 
127
      *path = apr_pstrcat(ctx->pool,*path,"/",dimval,NULL);
 
128
    }
 
129
  }
 
130
}
 
131
 
 
132
static void _mapcache_cache_disk_blank_tile_key(mapcache_context *ctx, mapcache_tile *tile, unsigned char *color, char **path)
 
133
{
 
134
  /* not implemented for template caches, as symlink_blank will never be set */
 
135
  *path = apr_psprintf(ctx->pool,"%s/%s/%s/blanks/%02X%02X%02X%02X.%s",
 
136
                       ((mapcache_cache_disk*)tile->tileset->cache)->base_directory,
 
137
                       tile->tileset->name,
 
138
                       tile->grid_link->grid->name,
 
139
                       color[0],
 
140
                       color[1],
 
141
                       color[2],
 
142
                       color[3],
 
143
                       tile->tileset->format?tile->tileset->format->extension:"png");
 
144
  if(!*path) {
 
145
    ctx->set_error(ctx,500, "failed to allocate blank tile key");
 
146
  }
 
147
}
 
148
 
 
149
/**
 
150
 * \brief return filename for given tile
 
151
 *
 
152
 * \param tile the tile to get the key from
 
153
 * \param path pointer to a char* that will contain the filename
 
154
 * \param r
 
155
 * \private \memberof mapcache_cache_disk
 
156
 */
 
157
static void _mapcache_cache_disk_tilecache_tile_key(mapcache_context *ctx, mapcache_tile *tile, char **path)
 
158
{
 
159
  mapcache_cache_disk *dcache = (mapcache_cache_disk*)tile->tileset->cache;
 
160
  if(dcache->base_directory) {
 
161
    char *start;
 
162
    _mapcache_cache_disk_base_tile_key(ctx, tile, &start);
 
163
    *path = apr_psprintf(ctx->pool,"%s/%02d/%03d/%03d/%03d/%03d/%03d/%03d.%s",
 
164
                         start,
 
165
                         tile->z,
 
166
                         tile->x / 1000000,
 
167
                         (tile->x / 1000) % 1000,
 
168
                         tile->x % 1000,
 
169
                         tile->y / 1000000,
 
170
                         (tile->y / 1000) % 1000,
 
171
                         tile->y % 1000,
 
172
                         tile->tileset->format?tile->tileset->format->extension:"png");
 
173
  } else {
 
174
    *path = dcache->filename_template;
 
175
    *path = mapcache_util_str_replace(ctx->pool,*path, "{tileset}", tile->tileset->name);
 
176
    *path = mapcache_util_str_replace(ctx->pool,*path, "{grid}", tile->grid_link->grid->name);
 
177
    *path = mapcache_util_str_replace(ctx->pool,*path, "{ext}",
 
178
                                      tile->tileset->format?tile->tileset->format->extension:"png");
 
179
    if(strstr(*path,"{x}"))
 
180
      *path = mapcache_util_str_replace(ctx->pool,*path, "{x}",
 
181
                                        apr_psprintf(ctx->pool,"%d",tile->x));
 
182
    else
 
183
      *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_x}",
 
184
                                        apr_psprintf(ctx->pool,"%d",
 
185
                                            tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1));
 
186
    if(strstr(*path,"{y}"))
 
187
      *path = mapcache_util_str_replace(ctx->pool,*path, "{y}",
 
188
                                        apr_psprintf(ctx->pool,"%d",tile->y));
 
189
    else
 
190
      *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_y}",
 
191
                                        apr_psprintf(ctx->pool,"%d",
 
192
                                            tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1));
 
193
    if(strstr(*path,"{z}"))
 
194
      *path = mapcache_util_str_replace(ctx->pool,*path, "{z}",
 
195
                                        apr_psprintf(ctx->pool,"%d",tile->z));
 
196
    else
 
197
      *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_z}",
 
198
                                        apr_psprintf(ctx->pool,"%d",
 
199
                                            tile->grid_link->grid->nlevels - tile->z - 1));
 
200
    if(tile->dimensions) {
 
201
      char *dimstring="";
 
202
      const apr_array_header_t *elts = apr_table_elts(tile->dimensions);
 
203
      int i = elts->nelts;
 
204
      while(i--) {
 
205
        apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,i,apr_table_entry_t));
 
206
        char *dimval = apr_pstrdup(ctx->pool,entry->val);
 
207
        char *iter = dimval;
 
208
        while(*iter) {
 
209
          /* replace dangerous characters by '#' */
 
210
          if(*iter == '.' || *iter == '/') {
 
211
            *iter = '#';
 
212
          }
 
213
          iter++;
 
214
        }
 
215
        dimstring = apr_pstrcat(ctx->pool,dimstring,"#",entry->key,"#",dimval,NULL);
 
216
      }
 
217
      *path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring);
 
218
    }
 
219
  }
 
220
  if(!*path) {
 
221
    ctx->set_error(ctx,500, "failed to allocate tile key");
 
222
  }
 
223
}
 
224
 
 
225
static void _mapcache_cache_disk_template_tile_key(mapcache_context *ctx, mapcache_tile *tile, char **path)
 
226
{
 
227
  mapcache_cache_disk *dcache = (mapcache_cache_disk*)tile->tileset->cache;
 
228
 
 
229
  *path = dcache->filename_template;
 
230
  *path = mapcache_util_str_replace(ctx->pool,*path, "{tileset}", tile->tileset->name);
 
231
  *path = mapcache_util_str_replace(ctx->pool,*path, "{grid}", tile->grid_link->grid->name);
 
232
  *path = mapcache_util_str_replace(ctx->pool,*path, "{ext}",
 
233
                                    tile->tileset->format?tile->tileset->format->extension:"png");
 
234
 
 
235
  if(strstr(*path,"{x}"))
 
236
    *path = mapcache_util_str_replace(ctx->pool,*path, "{x}",
 
237
                                      apr_psprintf(ctx->pool,"%d",tile->x));
 
238
  else
 
239
    *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_x}",
 
240
                                      apr_psprintf(ctx->pool,"%d",
 
241
                                          tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1));
 
242
  if(strstr(*path,"{y}"))
 
243
    *path = mapcache_util_str_replace(ctx->pool,*path, "{y}",
 
244
                                      apr_psprintf(ctx->pool,"%d",tile->y));
 
245
  else
 
246
    *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_y}",
 
247
                                      apr_psprintf(ctx->pool,"%d",
 
248
                                          tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1));
 
249
  if(strstr(*path,"{z}"))
 
250
    *path = mapcache_util_str_replace(ctx->pool,*path, "{z}",
 
251
                                      apr_psprintf(ctx->pool,"%d",tile->z));
 
252
  else
 
253
    *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_z}",
 
254
                                      apr_psprintf(ctx->pool,"%d",
 
255
                                          tile->grid_link->grid->nlevels - tile->z - 1));
 
256
  if(tile->dimensions) {
 
257
    char *dimstring="";
 
258
    const apr_array_header_t *elts = apr_table_elts(tile->dimensions);
 
259
    int i = elts->nelts;
 
260
    while(i--) {
 
261
      apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,i,apr_table_entry_t));
 
262
      char *dimval = apr_pstrdup(ctx->pool,entry->val);
 
263
      char *iter = dimval;
 
264
      while(*iter) {
 
265
        /* replace dangerous characters by '#' */
 
266
        if(*iter == '.' || *iter == '/') {
 
267
          *iter = '#';
 
268
        }
 
269
        iter++;
 
270
      }
 
271
      dimstring = apr_pstrcat(ctx->pool,dimstring,"#",entry->key,"#",dimval,NULL);
 
272
    }
 
273
    *path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring);
 
274
  }
 
275
 
 
276
  if(!*path) {
 
277
    ctx->set_error(ctx,500, "failed to allocate tile key");
 
278
  }
 
279
}
 
280
 
 
281
static void _mapcache_cache_disk_arcgis_tile_key(mapcache_context *ctx, mapcache_tile *tile, char **path)
 
282
{
 
283
  mapcache_cache_disk *dcache = (mapcache_cache_disk*)tile->tileset->cache;
 
284
  if(dcache->base_directory) {
 
285
    char *start;
 
286
    _mapcache_cache_disk_base_tile_key(ctx, tile, &start);
 
287
    *path = apr_psprintf(ctx->pool,"%s/L%02d/R%08x/C%08x.%s" ,
 
288
                         start,
 
289
                         tile->z,
 
290
                         tile->y,
 
291
                         tile->x,
 
292
                         tile->tileset->format?tile->tileset->format->extension:"png");
 
293
  }
 
294
 
 
295
  if(!*path) {
 
296
    ctx->set_error(ctx,500, "failed to allocate tile key");
 
297
  }
 
298
}
 
299
 
 
300
 
 
301
static int _mapcache_cache_disk_has_tile(mapcache_context *ctx, mapcache_tile *tile)
 
302
{
 
303
  char *filename;
 
304
  apr_finfo_t finfo;
 
305
  int rv;
 
306
  ((mapcache_cache_disk*)tile->tileset->cache)->tile_key(ctx, tile, &filename);
 
307
  if(GC_HAS_ERROR(ctx)) {
 
308
    return MAPCACHE_FALSE;
 
309
  }
 
310
  rv = apr_stat(&finfo,filename,0,ctx->pool);
 
311
  if(rv != APR_SUCCESS) {
 
312
    return MAPCACHE_FALSE;
 
313
  } else {
 
314
    return MAPCACHE_TRUE;
 
315
  }
 
316
}
 
317
 
 
318
static void _mapcache_cache_disk_delete(mapcache_context *ctx, mapcache_tile *tile)
 
319
{
 
320
  apr_status_t ret;
 
321
  char errmsg[120];
 
322
  char *filename;
 
323
  ((mapcache_cache_disk*)tile->tileset->cache)->tile_key(ctx, tile, &filename);
 
324
  GC_CHECK_ERROR(ctx);
 
325
 
 
326
  ret = apr_file_remove(filename,ctx->pool);
 
327
  if(ret != APR_SUCCESS && !APR_STATUS_IS_ENOENT(ret)) {
 
328
    ctx->set_error(ctx, 500,  "failed to remove file %s: %s",filename, apr_strerror(ret,errmsg,120));
 
329
  }
 
330
}
 
331
 
 
332
 
 
333
/**
 
334
 * \brief get file content of given tile
 
335
 *
 
336
 * fills the mapcache_tile::data of the given tile with content stored in the file
 
337
 * \private \memberof mapcache_cache_disk
 
338
 * \sa mapcache_cache::tile_get()
 
339
 */
 
340
static int _mapcache_cache_disk_get(mapcache_context *ctx, mapcache_tile *tile)
 
341
{
 
342
  char *filename;
 
343
  apr_file_t *f;
 
344
  apr_finfo_t finfo;
 
345
  apr_status_t rv;
 
346
  apr_size_t size;
 
347
  apr_mmap_t *tilemmap;
 
348
 
 
349
  ((mapcache_cache_disk*)tile->tileset->cache)->tile_key(ctx, tile, &filename);
 
350
  if(GC_HAS_ERROR(ctx)) {
 
351
    return MAPCACHE_FAILURE;
 
352
  }
 
353
  if((rv=apr_file_open(&f, filename,
 
354
#ifndef NOMMAP
 
355
                       APR_FOPEN_READ, APR_UREAD | APR_GREAD,
 
356
#else
 
357
                       APR_FOPEN_READ|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,APR_OS_DEFAULT,
 
358
#endif
 
359
                       ctx->pool)) == APR_SUCCESS) {
 
360
    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE|APR_FINFO_MTIME, f);
 
361
    if(!finfo.size) {
 
362
      ctx->set_error(ctx, 500, "tile %s has no data",filename);
 
363
      return MAPCACHE_FAILURE;
 
364
    }
 
365
 
 
366
    size = finfo.size;
 
367
    /*
 
368
     * at this stage, we have a handle to an open file that contains data.
 
369
     * idealy, we should aquire a read lock, in case the data contained inside the file
 
370
     * is incomplete (i.e. if another process is currently writing to the tile).
 
371
     * currently such a lock is not set, as we don't want to loose performance on tile accesses.
 
372
     * any error that might happen at this stage should only occur if the tile isn't already cached,
 
373
     * i.e. normally only once.
 
374
     */
 
375
    tile->mtime = finfo.mtime;
 
376
    tile->encoded_data = mapcache_buffer_create(size,ctx->pool);
 
377
 
 
378
#ifndef NOMMAP
 
379
 
 
380
    rv = apr_mmap_create(&tilemmap,f,0,finfo.size,APR_MMAP_READ,ctx->pool);
 
381
    if(rv != APR_SUCCESS) {
 
382
      char errmsg[120];
 
383
      ctx->set_error(ctx, 500,  "mmap error: %s",apr_strerror(rv,errmsg,120));
 
384
      return MAPCACHE_FAILURE;
 
385
    }
 
386
    tile->encoded_data->buf = tilemmap->mm;
 
387
    tile->encoded_data->size = tile->encoded_data->avail = finfo.size;
 
388
#else
 
389
    //manually add the data to our buffer
 
390
    apr_file_read(f,(void*)tile->encoded_data->buf,&size);
 
391
    tile->encoded_data->size = size;
 
392
    tile->encoded_data->avail = size;
 
393
#endif
 
394
    apr_file_close(f);
 
395
    if(tile->encoded_data->size != finfo.size) {
 
396
      ctx->set_error(ctx, 500,  "failed to copy image data, got %d of %d bytes",(int)size, (int)finfo.size);
 
397
      return MAPCACHE_FAILURE;
 
398
    }
 
399
    return MAPCACHE_SUCCESS;
 
400
  } else {
 
401
    if(APR_STATUS_IS_ENOENT(rv)) {
 
402
      /* the file doesn't exist on the disk */
 
403
      return MAPCACHE_CACHE_MISS;
 
404
    } else {
 
405
      char *error = strerror(rv);
 
406
      ctx->set_error(ctx, 500,  "failed to open file %s: %s",filename, error);
 
407
      return MAPCACHE_FAILURE;
 
408
    }
 
409
  }
 
410
}
 
411
 
 
412
/**
 
413
 * \brief write tile data to disk
 
414
 *
 
415
 * writes the content of mapcache_tile::data to disk.
 
416
 * \returns MAPCACHE_FAILURE if there is no data to write, or if the tile isn't locked
 
417
 * \returns MAPCACHE_SUCCESS if the tile has been successfully written to disk
 
418
 * \private \memberof mapcache_cache_disk
 
419
 * \sa mapcache_cache::tile_set()
 
420
 */
 
421
static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_tile *tile)
 
422
{
 
423
  apr_size_t bytes;
 
424
  apr_file_t *f;
 
425
  apr_status_t ret;
 
426
  char errmsg[120];
 
427
  char *filename, *hackptr1, *hackptr2=NULL;
 
428
  const int creation_retry = ((mapcache_cache_disk*)tile->tileset->cache)->creation_retry;
 
429
  int retry_count_create_file = 0;
 
430
 
 
431
#ifdef DEBUG
 
432
  /* all this should be checked at a higher level */
 
433
  if(!tile->encoded_data && !tile->raw_image) {
 
434
    ctx->set_error(ctx,500,"attempting to write empty tile to disk");
 
435
    return;
 
436
  }
 
437
  if(!tile->encoded_data && !tile->tileset->format) {
 
438
    ctx->set_error(ctx,500,"received a raw tile image for a tileset with no format");
 
439
    return;
 
440
  }
 
441
#endif
 
442
 
 
443
  ((mapcache_cache_disk*)tile->tileset->cache)->tile_key(ctx, tile, &filename);
 
444
  GC_CHECK_ERROR(ctx);
 
445
 
 
446
  /* find the location of the last '/' in the string */
 
447
  hackptr1 = filename;
 
448
  while(*hackptr1) {
 
449
    if(*hackptr1 == '/')
 
450
      hackptr2 = hackptr1;
 
451
    hackptr1++;
 
452
  }
 
453
  *hackptr2 = '\0';
 
454
 
 
455
  if(APR_SUCCESS != (ret = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) {
 
456
    /*
 
457
     * apr_dir_make_recursive sometimes sends back this error, although it should not.
 
458
     * ignore this one
 
459
     */
 
460
    if(!APR_STATUS_IS_EEXIST(ret)) {
 
461
      ctx->set_error(ctx, 500, "failed to create directory %s: %s",filename, apr_strerror(ret,errmsg,120));
 
462
      return;
 
463
    }
 
464
  }
 
465
  *hackptr2 = '/';
 
466
 
 
467
  ret = apr_file_remove(filename,ctx->pool);
 
468
  if(ret != APR_SUCCESS && !APR_STATUS_IS_ENOENT(ret)) {
 
469
    ctx->set_error(ctx, 500,  "failed to remove file %s: %s",filename, apr_strerror(ret,errmsg,120));
 
470
  }
 
471
 
 
472
 
 
473
#ifdef HAVE_SYMLINK
 
474
  if(((mapcache_cache_disk*)tile->tileset->cache)->symlink_blank) {
 
475
    if(!tile->raw_image) {
 
476
      tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
 
477
      GC_CHECK_ERROR(ctx);
 
478
    }
 
479
    if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) {
 
480
      char *blankname;
 
481
      _mapcache_cache_disk_blank_tile_key(ctx,tile,tile->raw_image->data,&blankname);
 
482
      if(apr_file_open(&f, blankname, APR_FOPEN_READ, APR_OS_DEFAULT, ctx->pool) != APR_SUCCESS) {
 
483
        if(!tile->encoded_data) {
 
484
          tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
 
485
          GC_CHECK_ERROR(ctx);
 
486
        }
 
487
        /* create the blank file */
 
488
        char *blankdirname = apr_psprintf(ctx->pool, "%s/%s/%s/blanks",
 
489
                                          ((mapcache_cache_disk*)tile->tileset->cache)->base_directory,
 
490
                                          tile->tileset->name,
 
491
                                          tile->grid_link->grid->name);
 
492
        if(APR_SUCCESS != (ret = apr_dir_make_recursive(
 
493
                                   blankdirname, APR_OS_DEFAULT,ctx->pool))) {
 
494
          if(!APR_STATUS_IS_EEXIST(ret)) {
 
495
            ctx->set_error(ctx, 500,  "failed to create directory %s for blank tiles",blankdirname, apr_strerror(ret,errmsg,120));
 
496
            return;
 
497
          }
 
498
        }
 
499
 
 
500
        /* aquire a lock on the blank file */
 
501
        int isLocked = mapcache_lock_or_wait_for_resource(ctx,blankname);
 
502
 
 
503
        if(isLocked == MAPCACHE_TRUE) {
 
504
 
 
505
          if((ret = apr_file_open(&f, blankname,
 
506
                                  APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,
 
507
                                  APR_OS_DEFAULT, ctx->pool)) != APR_SUCCESS) {
 
508
            ctx->set_error(ctx, 500,  "failed to create file %s: %s",blankname, apr_strerror(ret,errmsg,120));
 
509
            mapcache_unlock_resource(ctx,blankname);
 
510
            return; /* we could not create the file */
 
511
          }
 
512
 
 
513
          bytes = (apr_size_t)tile->encoded_data->size;
 
514
          ret = apr_file_write(f,(void*)tile->encoded_data->buf,&bytes);
 
515
          if(ret != APR_SUCCESS) {
 
516
            ctx->set_error(ctx, 500,  "failed to write data to file %s (wrote %d of %d bytes): %s",blankname, (int)bytes, (int)tile->encoded_data->size, apr_strerror(ret,errmsg,120));
 
517
            mapcache_unlock_resource(ctx,blankname);
 
518
            return; /* we could not create the file */
 
519
          }
 
520
 
 
521
          if(bytes != tile->encoded_data->size) {
 
522
            ctx->set_error(ctx, 500,  "failed to write image data to %s, wrote %d of %d bytes", blankname, (int)bytes, (int)tile->encoded_data->size);
 
523
            mapcache_unlock_resource(ctx,blankname);
 
524
            return;
 
525
          }
 
526
          apr_file_close(f);
 
527
          mapcache_unlock_resource(ctx,blankname);
 
528
#ifdef DEBUG
 
529
          ctx->log(ctx,MAPCACHE_DEBUG,"created blank tile %s",blankname);
 
530
#endif
 
531
        }
 
532
      } else {
 
533
        apr_file_close(f);
 
534
      }
 
535
 
 
536
      int retry_count_create_symlink = 0;
 
537
 
 
538
      /*
 
539
       * compute the relative path between tile and blank tile
 
540
       */
 
541
      char *blankname_rel = NULL;
 
542
      blankname_rel = relative_path(ctx,filename, blankname);
 
543
      GC_CHECK_ERROR(ctx);
 
544
 
 
545
      /*
 
546
       * depending on configuration symlink creation will retry if it fails.
 
547
       * this can happen on nfs mounted network storage.
 
548
       * the solution is to create the containing directory again and retry the symlink creation.
 
549
       */
 
550
      while(symlink(blankname_rel, filename) != 0) {
 
551
        retry_count_create_symlink++;
 
552
 
 
553
        if(retry_count_create_symlink > creation_retry) {
 
554
          char *error = strerror(errno);
 
555
          ctx->set_error(ctx, 500, "failed to link tile %s to %s: %s",filename, blankname_rel, error);
 
556
          return; /* we could not create the file */
 
557
        }
 
558
 
 
559
        *hackptr2 = '\0';
 
560
 
 
561
        if(APR_SUCCESS != (ret = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) {
 
562
          if(!APR_STATUS_IS_EEXIST(ret)) {
 
563
            ctx->set_error(ctx, 500, "failed to create symlink, can not create directory %s: %s",filename, apr_strerror(ret,errmsg,120));
 
564
            return; /* we could not create the file */
 
565
          }
 
566
        }
 
567
 
 
568
        *hackptr2 = '/';
 
569
      }
 
570
#ifdef DEBUG
 
571
      ctx->log(ctx, MAPCACHE_DEBUG, "linked blank tile %s to %s",filename,blankname);
 
572
#endif
 
573
      return;
 
574
    }
 
575
  }
 
576
#endif /*HAVE_SYMLINK*/
 
577
 
 
578
  /* go the normal way: either we haven't configured blank tile detection, or the tile was not blank */
 
579
 
 
580
  if(!tile->encoded_data) {
 
581
    tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
 
582
    GC_CHECK_ERROR(ctx);
 
583
  }
 
584
 
 
585
  /*
 
586
   * depending on configuration file creation will retry if it fails.
 
587
   * this can happen on nfs mounted network storage.
 
588
   * the solution is to create the containing directory again and retry the file creation.
 
589
   */
 
590
  while((ret = apr_file_open(&f, filename,
 
591
                             APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,
 
592
                             APR_OS_DEFAULT, ctx->pool)) != APR_SUCCESS) {
 
593
 
 
594
    retry_count_create_file++;
 
595
 
 
596
    if(retry_count_create_file > creation_retry) {
 
597
      ctx->set_error(ctx, 500, "failed to create file %s: %s",filename, apr_strerror(ret,errmsg,120));
 
598
      return; /* we could not create the file */
 
599
    }
 
600
 
 
601
    *hackptr2 = '\0';
 
602
 
 
603
    if(APR_SUCCESS != (ret = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) {
 
604
      if(!APR_STATUS_IS_EEXIST(ret)) {
 
605
        ctx->set_error(ctx, 500, "failed to create file, can not create directory %s: %s",filename, apr_strerror(ret,errmsg,120));
 
606
        return; /* we could not create the file */
 
607
      }
 
608
    }
 
609
 
 
610
    *hackptr2 = '/';
 
611
  }
 
612
 
 
613
  bytes = (apr_size_t)tile->encoded_data->size;
 
614
  ret = apr_file_write(f,(void*)tile->encoded_data->buf,&bytes);
 
615
  if(ret != APR_SUCCESS) {
 
616
    ctx->set_error(ctx, 500,  "failed to write data to file %s (wrote %d of %d bytes): %s",filename, (int)bytes, (int)tile->encoded_data->size, apr_strerror(ret,errmsg,120));
 
617
    return; /* we could not create the file */
 
618
  }
 
619
 
 
620
  if(bytes != tile->encoded_data->size) {
 
621
    ctx->set_error(ctx, 500, "failed to write image data to %s, wrote %d of %d bytes", filename, (int)bytes, (int)tile->encoded_data->size);
 
622
  }
 
623
  ret = apr_file_close(f);
 
624
  if(ret != APR_SUCCESS) {
 
625
    ctx->set_error(ctx, 500,  "failed to close file %s:%s",filename, apr_strerror(ret,errmsg,120));
 
626
    return; /* we could not create the file */
 
627
  }
 
628
 
 
629
}
 
630
 
 
631
/**
 
632
 * \private \memberof mapcache_cache_disk
 
633
 */
 
634
static void _mapcache_cache_disk_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config)
 
635
{
 
636
  ezxml_t cur_node;
 
637
  mapcache_cache_disk *dcache = (mapcache_cache_disk*)cache;
 
638
  char *layout = NULL;
 
639
  int template_layout = MAPCACHE_FALSE;
 
640
 
 
641
  layout = (char*)ezxml_attr(node,"layout");
 
642
  if (!layout || !strlen(layout) || !strcmp(layout,"tilecache")) {
 
643
    dcache->tile_key = _mapcache_cache_disk_tilecache_tile_key;
 
644
  } else if(!strcmp(layout,"arcgis")) {
 
645
    dcache->tile_key = _mapcache_cache_disk_arcgis_tile_key;
 
646
  } else if (!strcmp(layout,"template")) {
 
647
    dcache->tile_key = _mapcache_cache_disk_template_tile_key;
 
648
    template_layout = MAPCACHE_TRUE;
 
649
    if ((cur_node = ezxml_child(node,"template")) != NULL) {
 
650
      dcache->filename_template = apr_pstrdup(ctx->pool,cur_node->txt);
 
651
    } else {
 
652
      ctx->set_error(ctx, 400, "no template specified for cache \"%s\"", cache->name);
 
653
      return;
 
654
    }
 
655
  } else {
 
656
    ctx->set_error(ctx, 400, "unknown layout type %s for cache \"%s\"", layout, cache->name);
 
657
    return;
 
658
  }
 
659
 
 
660
  if (!template_layout && (cur_node = ezxml_child(node,"base")) != NULL) {
 
661
    dcache->base_directory = apr_pstrdup(ctx->pool,cur_node->txt);
 
662
  }
 
663
 
 
664
  if (!template_layout && (cur_node = ezxml_child(node,"symlink_blank")) != NULL) {
 
665
    if(strcasecmp(cur_node->txt,"false")) {
 
666
#ifdef HAVE_SYMLINK
 
667
      dcache->symlink_blank=1;
 
668
#else
 
669
      ctx->set_error(ctx,400,"cache %s: host system does not support file symbolic linking",cache->name);
 
670
      return;
 
671
#endif
 
672
    }
 
673
  }
 
674
 
 
675
  if ((cur_node = ezxml_child(node,"creation_retry")) != NULL) {
 
676
    dcache->creation_retry = atoi(cur_node->txt);
 
677
  }
 
678
}
 
679
 
 
680
/**
 
681
 * \private \memberof mapcache_cache_disk
 
682
 */
 
683
static void _mapcache_cache_disk_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache,
 
684
    mapcache_cfg *cfg)
 
685
{
 
686
  mapcache_cache_disk *dcache = (mapcache_cache_disk*)cache;
 
687
  /* check all required parameters are configured */
 
688
  if((!dcache->base_directory || !strlen(dcache->base_directory)) &&
 
689
      (!dcache->filename_template || !strlen(dcache->filename_template))) {
 
690
    ctx->set_error(ctx, 400, "disk cache %s has no base directory or template",dcache->cache.name);
 
691
    return;
 
692
  }
 
693
}
 
694
 
 
695
/**
 
696
 * \brief creates and initializes a mapcache_disk_cache
 
697
 */
 
698
mapcache_cache* mapcache_cache_disk_create(mapcache_context *ctx)
 
699
{
 
700
  mapcache_cache_disk *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_disk));
 
701
  if(!cache) {
 
702
    ctx->set_error(ctx, 500, "failed to allocate disk cache");
 
703
    return NULL;
 
704
  }
 
705
  cache->symlink_blank = 0;
 
706
  cache->creation_retry = 0;
 
707
  cache->cache.metadata = apr_table_make(ctx->pool,3);
 
708
  cache->cache.type = MAPCACHE_CACHE_DISK;
 
709
  cache->cache.tile_delete = _mapcache_cache_disk_delete;
 
710
  cache->cache.tile_get = _mapcache_cache_disk_get;
 
711
  cache->cache.tile_exists = _mapcache_cache_disk_has_tile;
 
712
  cache->cache.tile_set = _mapcache_cache_disk_set;
 
713
  cache->cache.configuration_post_config = _mapcache_cache_disk_configuration_post_config;
 
714
  cache->cache.configuration_parse_xml = _mapcache_cache_disk_configuration_parse_xml;
 
715
  return (mapcache_cache*)cache;
 
716
}
 
717
 
 
718
/* vim: ts=2 sts=2 et sw=2
 
719
*/