1 /* -*- c-basic-offset: 2 -*- */
2 /*
3   Copyright(C) 2009-2017 Brazil
4 
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License version 2.1 as published by the Free Software Foundation.
8 
9   This library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Lesser General Public License for more details.
13 
14   You should have received a copy of the GNU Lesser General Public
15   License along with this library; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
17 */
18 
19 #include "grn.h"
20 
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 
27 #include "grn_ctx.h"
28 #include "grn_io.h"
29 #include "grn_plugin.h"
30 #include "grn_hash.h"
31 #include "grn_ctx_impl.h"
32 #include "grn_util.h"
33 
34 #ifdef WIN32
35 # include <io.h>
36 # include <share.h>
37 #endif /* WIN32 */
38 
39 #define GRN_IO_IDSTR "GROONGA:IO:00001"
40 #define GRN_IO_IDSTR_LEN (sizeof(GRN_IO_IDSTR) - 1)
41 
42 #define GRN_IO_VERSION_DEFAULT 1
43 
44 #define GRN_IO_FILE_SIZE_V1 1073741824UL
45 
46 #ifdef WIN32
47 # define GRN_IO_FILE_SIZE_V0  134217728L
48 #else /* WIN32 */
49 # define GRN_IO_FILE_SIZE_V0  GRN_IO_FILE_SIZE_V1
50 #endif /* WIN32 */
51 
52 typedef struct _grn_io_fileinfo {
53 #ifdef WIN32
54   HANDLE fh;
55   HANDLE fmo;
56   grn_critical_section cs;
57 #else /* WIN32 */
58   int fd;
59   dev_t dev;
60   ino_t inode;
61 #endif /* WIN32 */
62 } fileinfo;
63 
64 #define IO_HEADER_SIZE 64
65 
66 static uint32_t grn_io_version_default = GRN_IO_VERSION_DEFAULT;
67 static grn_bool grn_io_use_sparse = GRN_FALSE;
68 
69 inline static grn_rc grn_fileinfo_open(grn_ctx *ctx, fileinfo *fi,
70                                        const char *path, int flags);
71 inline static void grn_fileinfo_init(fileinfo *fis, int nfis);
72 inline static int grn_fileinfo_opened(fileinfo *fi);
73 inline static grn_rc grn_fileinfo_close(grn_ctx *ctx, fileinfo *fi);
74 #ifdef WIN32
75 inline static void * grn_mmap(grn_ctx *ctx, grn_ctx *owner_ctx,
76                               grn_io *io, HANDLE *fmo, fileinfo *fi,
77                               off_t offset, size_t length);
78 inline static int grn_munmap(grn_ctx *ctx, grn_ctx *owner_ctx,
79                              grn_io *io, HANDLE *fmo, fileinfo *fi,
80                              void *start, size_t length);
81 inline static int grn_msync(grn_ctx *ctx, HANDLE fh,
82                             void *start, size_t length);
83 # define GRN_MMAP(ctx,owner_ctx,io,fmo,fi,offset,length)\
84   (grn_mmap((ctx), (owner_ctx), (io), (fmo), (fi), (offset), (length)))
85 # define GRN_MUNMAP(ctx,owner_ctx,io,fmo,fi,start,length)\
86   (grn_munmap((ctx), (owner_ctx), (io), (fmo), (fi), (start), (length)))
87 # define GRN_MSYNC(ctx,fh,start,length) \
88   (grn_msync((ctx), (fh), (start), (length)))
89 #else /* WIN32 */
90 inline static void * grn_mmap(grn_ctx *ctx, grn_ctx *owner_ctx,
91                               grn_io *io, fileinfo *fi,
92                               off_t offset, size_t length);
93 inline static int grn_munmap(grn_ctx *ctx, grn_ctx *owner_ctx,
94                              grn_io *io, fileinfo *fi,
95                              void *start, size_t length);
96 inline static int grn_msync(grn_ctx *ctx, void *start, size_t length);
97 # define GRN_MUNMAP(ctx,owner_ctx,io,fmo,fi,start,length) \
98   (grn_munmap((ctx), (owner_ctx), (io), (fi), (start), (length)))
99 # define GRN_MSYNC(ctx,fh,start,length) \
100   (grn_msync((ctx), (start), (length)))
101 # ifdef USE_FAIL_MALLOC
102 inline static void * grn_fail_mmap(grn_ctx *ctx, grn_ctx *owner_ctx,
103                                    grn_io *io, fileinfo *fi,
104                                    off_t offset, size_t length,
105                                    const char* file, int line, const char *func);
106 #  define GRN_MMAP(ctx,owner_ctx,io,fmo,fi,offset,length)\
107   (grn_fail_mmap((ctx), (owner_ctx), (io), (fi), (offset), (length),\
108                  __FILE__, __LINE__, __FUNCTION__))
109 # else /* USE_FAIL_MALLOC */
110 #  define GRN_MMAP(ctx,owner_ctx,io,fmo,fi,offset,length)\
111   (grn_mmap((ctx), (owner_ctx), (io), (fi), (offset), (length)))
112 # endif /* USE_FAIL_MALLOC */
113 #endif  /* WIN32 */
114 inline static grn_rc grn_pread(grn_ctx *ctx, fileinfo *fi, void *buf,
115                                size_t count, off_t offset);
116 inline static grn_rc grn_pwrite(grn_ctx *ctx, fileinfo *fi, void *buf,
117                                 size_t count, off_t offset);
118 
119 void
grn_io_init_from_env(void)120 grn_io_init_from_env(void)
121 {
122   {
123     char version_env[GRN_ENV_BUFFER_SIZE];
124 
125     grn_getenv("GRN_IO_VERSION",
126                version_env,
127                GRN_ENV_BUFFER_SIZE);
128     if (version_env[0]) {
129       grn_io_version_default = atoi(version_env);
130     }
131   }
132 
133   {
134     char use_sparse_env[GRN_ENV_BUFFER_SIZE];
135 
136     grn_getenv("GRN_IO_USE_SPARSE",
137                use_sparse_env,
138                GRN_ENV_BUFFER_SIZE);
139     if (use_sparse_env[0] && strcmp(use_sparse_env, "yes") == 0) {
140       grn_io_use_sparse = GRN_TRUE;
141     }
142   }
143 }
144 
145 static inline uint32_t
grn_io_compute_base(uint32_t header_size)146 grn_io_compute_base(uint32_t header_size)
147 {
148   uint32_t total_header_size;
149   total_header_size = IO_HEADER_SIZE + header_size;
150   return (total_header_size + grn_pagesize - 1) & ~(grn_pagesize - 1);
151 }
152 
153 static inline uint32_t
grn_io_compute_base_segment(uint32_t base,uint32_t segment_size)154 grn_io_compute_base_segment(uint32_t base, uint32_t segment_size)
155 {
156   return (base + segment_size - 1) / segment_size;
157 }
158 
159 static uint32_t
grn_io_compute_max_n_files(uint32_t segment_size,uint32_t max_segment,unsigned int base_segument,unsigned long file_size)160 grn_io_compute_max_n_files(uint32_t segment_size, uint32_t max_segment,
161                            unsigned int base_segument, unsigned long file_size)
162 {
163   uint64_t last_segment_end;
164   last_segment_end = ((uint64_t)segment_size) * (max_segment + base_segument);
165   return (uint32_t)((last_segment_end + file_size - 1) / file_size);
166 }
167 
168 static inline unsigned long
grn_io_compute_file_size(uint32_t version)169 grn_io_compute_file_size(uint32_t version)
170 {
171   if (version == 0) {
172     return GRN_IO_FILE_SIZE_V0;
173   } else {
174     return GRN_IO_FILE_SIZE_V1;
175   }
176 }
177 
178 static inline uint32_t
grn_io_max_segment(grn_io * io)179 grn_io_max_segment(grn_io *io)
180 {
181   if (io->header->segment_tail) {
182     return io->header->segment_tail;
183   } else {
184     return io->header->max_segment;
185   }
186 }
187 
188 static uint32_t
grn_io_max_n_files(grn_io * io)189 grn_io_max_n_files(grn_io *io)
190 {
191   unsigned long file_size;
192 
193   file_size = grn_io_compute_file_size(io->header->version);
194   return grn_io_compute_max_n_files(io->header->segment_size,
195                                     grn_io_max_segment(io),
196                                     io->base_seg,
197                                     file_size);
198 }
199 
200 static inline uint32_t
grn_io_compute_nth_file_info(grn_io * io,uint32_t nth_segment)201 grn_io_compute_nth_file_info(grn_io *io, uint32_t nth_segment)
202 {
203   uint32_t segment_size;
204   unsigned long file_size;
205   uint32_t segments_per_file;
206   uint32_t resolved_nth_segment;
207 
208   segment_size = io->header->segment_size;
209   file_size = grn_io_compute_file_size(io->header->version);
210   segments_per_file = file_size / segment_size;
211   resolved_nth_segment = nth_segment + io->base_seg;
212   return resolved_nth_segment / segments_per_file;
213 }
214 
215 static grn_io *
grn_io_create_tmp(grn_ctx * ctx,uint32_t header_size,uint32_t segment_size,uint32_t max_segment,grn_io_mode mode,uint32_t flags)216 grn_io_create_tmp(grn_ctx *ctx, uint32_t header_size, uint32_t segment_size,
217                   uint32_t max_segment, grn_io_mode mode, uint32_t flags)
218 {
219   grn_io *io;
220   uint32_t b;
221   struct _grn_io_header *header;
222   b = grn_io_compute_base(header_size);
223   header = (struct _grn_io_header *)GRN_MMAP(ctx, &grn_gctx, NULL, NULL, NULL,
224                                              0, b);
225   if (header) {
226     header->version = grn_io_version_default;
227     header->header_size = header_size;
228     header->segment_size = segment_size;
229     header->max_segment = max_segment;
230     header->n_arrays = 0;
231     header->flags = flags;
232     header->lock = 0;
233     grn_memcpy(header->idstr, GRN_IO_IDSTR, 16);
234     if ((io = GRN_MALLOCN(grn_io, 1))) {
235       grn_io_mapinfo *maps = NULL;
236       if ((maps = GRN_CALLOC(sizeof(grn_io_mapinfo) * max_segment))) {
237         io->header = header;
238         io->user_header = (((byte *) header) + IO_HEADER_SIZE);
239         io->maps = maps;
240         io->base = b;
241         io->base_seg = 0;
242         io->mode = mode;
243         io->header->curr_size = b;
244         io->fis = NULL;
245         io->ainfo = NULL;
246         io->max_map_seg = 0;
247         io->nmaps = 0;
248         io->count = 0;
249         io->flags = GRN_IO_TEMPORARY;
250         io->lock = &header->lock;
251         io->path[0] = '\0';
252         return io;
253       }
254       GRN_FREE(io);
255     }
256     GRN_MUNMAP(ctx, &grn_gctx, NULL, NULL, NULL, header, b);
257   }
258   return NULL;
259 }
260 
261 static void
grn_io_register(grn_ctx * ctx,grn_io * io)262 grn_io_register(grn_ctx *ctx, grn_io *io)
263 {
264   if (io->fis && (io->flags & (GRN_IO_EXPIRE_GTICK|GRN_IO_EXPIRE_SEGMENT))) {
265     grn_bool succeeded = GRN_FALSE;
266     CRITICAL_SECTION_ENTER(grn_glock);
267     if (grn_gctx.impl && grn_gctx.impl->ios &&
268         grn_hash_add(&grn_gctx, grn_gctx.impl->ios, io->path, strlen(io->path),
269                      (void **)&io, NULL)) {
270       succeeded = GRN_TRUE;
271     }
272     CRITICAL_SECTION_LEAVE(grn_glock);
273     if (!succeeded) {
274       GRN_LOG(ctx, GRN_LOG_WARNING,
275               "grn_io_register(%s) failed", io->path);
276     }
277   }
278 }
279 
280 static void
grn_io_unregister(grn_ctx * ctx,grn_io * io)281 grn_io_unregister(grn_ctx *ctx, grn_io *io)
282 {
283   if (io->fis && (io->flags & (GRN_IO_EXPIRE_GTICK|GRN_IO_EXPIRE_SEGMENT))) {
284     grn_bool succeeded = GRN_FALSE;
285     CRITICAL_SECTION_ENTER(grn_glock);
286     if (grn_gctx.impl && grn_gctx.impl->ios) {
287       grn_hash_delete(&grn_gctx, grn_gctx.impl->ios,
288                       io->path, strlen(io->path), NULL);
289       succeeded = GRN_TRUE;
290     }
291     CRITICAL_SECTION_LEAVE(grn_glock);
292     if (!succeeded) {
293       GRN_LOG(ctx, GRN_LOG_WARNING,
294               "grn_io_unregister(%s) failed", io->path);
295     }
296   }
297 }
298 
299 grn_io *
grn_io_create(grn_ctx * ctx,const char * path,uint32_t header_size,uint32_t segment_size,uint32_t max_segment,grn_io_mode mode,uint32_t flags)300 grn_io_create(grn_ctx *ctx, const char *path, uint32_t header_size,
301               uint32_t segment_size, uint32_t max_segment, grn_io_mode mode,
302               uint32_t flags)
303 {
304   grn_io *io;
305   fileinfo *fis;
306   uint32_t b, max_nfiles;
307   uint32_t bs;
308   struct _grn_io_header *header;
309   uint32_t version = grn_io_version_default;
310   unsigned long file_size;
311 
312   if (!path) {
313     return grn_io_create_tmp(ctx, header_size, segment_size, max_segment,
314                              mode, flags);
315   }
316   if (!*path || (strlen(path) > PATH_MAX - 4)) { return NULL; }
317   b = grn_io_compute_base(header_size);
318   bs = grn_io_compute_base_segment(b, segment_size);
319   file_size = grn_io_compute_file_size(version);
320   max_nfiles = grn_io_compute_max_n_files(segment_size, max_segment,
321                                           bs, file_size);
322   if ((fis = GRN_MALLOCN(fileinfo, max_nfiles))) {
323     grn_fileinfo_init(fis, max_nfiles);
324     if (!grn_fileinfo_open(ctx, fis, path, O_RDWR|O_CREAT|O_EXCL)) {
325       header = (struct _grn_io_header *)GRN_MMAP(ctx, &grn_gctx, NULL,
326                                                  &fis->fmo, fis, 0, b);
327       if (header) {
328         header->version = version;
329         header->header_size = header_size;
330         header->segment_size = segment_size;
331         header->max_segment = max_segment;
332         header->n_arrays = 0;
333         header->flags = flags;
334         header->lock = 0;
335         grn_memcpy(header->idstr, GRN_IO_IDSTR, 16);
336         GRN_MSYNC(ctx, fis[0].fh, header, b);
337         if ((io = GRN_MALLOCN(grn_io, 1))) {
338           grn_io_mapinfo *maps = NULL;
339           if ((maps = GRN_CALLOC(sizeof(grn_io_mapinfo) * max_segment))) {
340             grn_strncpy(io->path, PATH_MAX, path, PATH_MAX);
341             io->header = header;
342             io->user_header = (((byte *) header) + IO_HEADER_SIZE);
343             io->maps = maps;
344             io->base = b;
345             io->base_seg = bs;
346             io->mode = mode;
347             io->header->curr_size = b;
348             io->fis = fis;
349             io->ainfo = NULL;
350             io->max_map_seg = 0;
351             io->nmaps = 0;
352             io->count = 0;
353             io->flags = flags;
354             io->lock = &header->lock;
355             grn_io_register(ctx, io);
356             return io;
357           }
358           GRN_FREE(io);
359         }
360         GRN_MUNMAP(ctx, &grn_gctx, NULL, &fis->fmo, fis, header, b);
361       }
362       grn_fileinfo_close(ctx, fis);
363       if (grn_unlink(path) == 0) {
364         GRN_LOG(ctx, GRN_LOG_INFO,
365                 "[io][create][error] removed path: <%s>", path);
366       } else {
367         ERRNO_ERR("[io][create][error] failed to remove path: <%s>", path);
368       }
369     }
370     GRN_FREE(fis);
371   }
372   return NULL;
373 }
374 
375 static grn_rc
array_init_(grn_ctx * ctx,grn_io * io,int n_arrays,size_t hsize,size_t msize)376 array_init_(grn_ctx *ctx, grn_io *io, int n_arrays, size_t hsize, size_t msize)
377 {
378   int i;
379   uint32_t ws;
380   byte *hp, *mp;
381   grn_io_array_spec *array_specs = (grn_io_array_spec *)io->user_header;
382   hp = io->user_header;
383   if (!(mp = GRN_CALLOC(msize))) {
384     return GRN_NO_MEMORY_AVAILABLE;
385   }
386   io->ainfo = (grn_io_array_info *)mp;
387   hp += sizeof(grn_io_array_spec) * n_arrays;
388   mp += sizeof(grn_io_array_info) * n_arrays;
389   for (ws = 0; (1 << ws) < io->header->segment_size; ws++);
390   for (i = 0; i < n_arrays; i++) {
391     uint32_t we = ws - array_specs[i].w_of_element;
392     io->ainfo[i].w_of_elm_in_a_segment = we;
393     io->ainfo[i].elm_mask_in_a_segment = (1 << we) - 1;
394     io->ainfo[i].max_n_segments = array_specs[i].max_n_segments;
395     io->ainfo[i].element_size = 1 << array_specs[i].w_of_element;
396     io->ainfo[i].segments = (uint32_t *)hp;
397     io->ainfo[i].addrs = (void **)mp;
398     hp += sizeof(uint32_t) * array_specs[i].max_n_segments;
399     mp += sizeof(void *) * array_specs[i].max_n_segments;
400   }
401   io->user_header += hsize;
402   return GRN_SUCCESS;
403 }
404 
405 static grn_rc
array_init(grn_ctx * ctx,grn_io * io,int n_arrays)406 array_init(grn_ctx *ctx, grn_io *io, int n_arrays)
407 {
408   if (n_arrays) {
409     int i;
410     grn_io_array_spec *array_specs = (grn_io_array_spec *)io->user_header;
411     size_t hsize = sizeof(grn_io_array_spec) * n_arrays;
412     size_t msize = sizeof(grn_io_array_info) * n_arrays;
413     for (i = 0; i < n_arrays; i++) {
414       hsize += sizeof(uint32_t) * array_specs[i].max_n_segments;
415       msize += sizeof(void *) * array_specs[i].max_n_segments;
416     }
417     return array_init_(ctx, io, n_arrays, hsize, msize);
418   }
419   return GRN_SUCCESS;
420 }
421 
422 grn_io *
grn_io_create_with_array(grn_ctx * ctx,const char * path,uint32_t header_size,uint32_t segment_size,grn_io_mode mode,int n_arrays,grn_io_array_spec * array_specs)423 grn_io_create_with_array(grn_ctx *ctx, const char *path,
424                          uint32_t header_size, uint32_t segment_size,
425                          grn_io_mode mode, int n_arrays,
426                          grn_io_array_spec *array_specs)
427 {
428   if (n_arrays) {
429     int i;
430     grn_io *io;
431     byte *hp;
432     uint32_t nsegs = 0;
433     size_t hsize = sizeof(grn_io_array_spec) * n_arrays;
434     size_t msize = sizeof(grn_io_array_info) * n_arrays;
435     for (i = 0; i < n_arrays; i++) {
436       nsegs += array_specs[i].max_n_segments;
437       hsize += sizeof(uint32_t) * array_specs[i].max_n_segments;
438       msize += sizeof(void *) * array_specs[i].max_n_segments;
439     }
440     if ((io = grn_io_create(ctx, path, header_size + hsize,
441                             segment_size, nsegs, mode, GRN_IO_EXPIRE_GTICK))) {
442       grn_rc rc;
443       hp = io->user_header;
444       grn_memcpy(hp, array_specs, sizeof(grn_io_array_spec) * n_arrays);
445       io->header->n_arrays = n_arrays;
446       io->header->segment_tail = 1;
447       rc = array_init_(ctx, io, n_arrays, hsize, msize);
448       if (rc == GRN_SUCCESS) {
449         return io;
450       }
451       ERR(GRN_NO_MEMORY_AVAILABLE, "grn_io_create_with_array failed");
452       grn_io_close(ctx, io);
453     }
454   }
455   return NULL;
456 }
457 
458 inline static uint32_t
segment_alloc(grn_ctx * ctx,grn_io * io)459 segment_alloc(grn_ctx *ctx, grn_io *io)
460 {
461   uint32_t n, s;
462   grn_io_array_info *ai;
463   if (io->header->segment_tail) {
464     if (io->header->segment_tail > io->header->max_segment) {
465       s = 0;
466     } else {
467       s = io->header->segment_tail++;
468     }
469   } else {
470     char *used = GRN_CALLOC(io->header->max_segment + 1);
471     if (!used) { return 0; }
472     for (n = io->header->n_arrays, ai = io->ainfo; n; n--, ai++) {
473       for (s = 0; s < ai->max_n_segments; s++) {
474         used[ai->segments[s]] = 1;
475       }
476     }
477     for (s = 1; ; s++) {
478       if (s > io->header->max_segment) {
479         io->header->segment_tail = s;
480         s = 0;
481         break;
482       }
483       if (!used[s]) {
484         io->header->segment_tail = s + 1;
485         break;
486       }
487     }
488     GRN_FREE(used);
489   }
490   return s;
491 }
492 
493 void
grn_io_segment_alloc(grn_ctx * ctx,grn_io * io,grn_io_array_info * ai,uint32_t lseg,int * flags,void ** p)494 grn_io_segment_alloc(grn_ctx *ctx, grn_io *io, grn_io_array_info *ai,
495                      uint32_t lseg, int *flags, void **p)
496 {
497   uint32_t *sp = &ai->segments[lseg];
498   if (!*sp) {
499     if ((*flags & GRN_TABLE_ADD)) {
500       if ((*sp = segment_alloc(ctx, io))) {
501         *flags |= GRN_TABLE_ADDED;
502       }
503     }
504   }
505   if (*sp) {
506     uint32_t pseg = *sp - 1;
507     GRN_IO_SEG_REF(io, pseg, *p);
508     if (*p) { GRN_IO_SEG_UNREF(io, pseg); };
509   }
510 }
511 
512 void *
grn_io_array_at(grn_ctx * ctx,grn_io * io,uint32_t array,off_t offset,int * flags)513 grn_io_array_at(grn_ctx *ctx, grn_io *io, uint32_t array, off_t offset, int *flags)
514 {
515   void *res;
516   GRN_IO_ARRAY_AT(io,array,offset,flags,res);
517   return res;
518 }
519 
520 uint32_t
grn_io_detect_type(grn_ctx * ctx,const char * path)521 grn_io_detect_type(grn_ctx *ctx, const char *path)
522 {
523   struct _grn_io_header h;
524   uint32_t res = 0;
525   int fd;
526   grn_open(fd, path, O_RDONLY | GRN_OPEN_FLAG_BINARY);
527   if (fd != -1) {
528     struct stat s;
529     if (fstat(fd, &s) != -1 && s.st_size >= sizeof(struct _grn_io_header)) {
530       if (grn_read(fd, &h, sizeof(struct _grn_io_header)) ==
531           sizeof(struct _grn_io_header)) {
532         if (!memcmp(h.idstr, GRN_IO_IDSTR, GRN_IO_IDSTR_LEN)) {
533           res = h.type;
534         } else {
535           ERR(GRN_INCOMPATIBLE_FILE_FORMAT,
536               "failed to detect type: format ID is different: <%s>: <%.*s>",
537               path,
538               (int)GRN_IO_IDSTR_LEN, GRN_IO_IDSTR);
539         }
540       } else {
541         SERR("failed to read enough data for detecting type: <%s>",
542              path);
543       }
544     } else {
545       ERR(GRN_INVALID_FORMAT, "grn_io_detect_type failed");
546     }
547     grn_close(fd);
548   } else {
549     ERRNO_ERR("failed to open path for detecting type: <%s>",
550               path);
551   }
552   return res;
553 }
554 
555 grn_io *
grn_io_open(grn_ctx * ctx,const char * path,grn_io_mode mode)556 grn_io_open(grn_ctx *ctx, const char *path, grn_io_mode mode)
557 {
558   size_t max_path_len = PATH_MAX - 4;
559   grn_io *io;
560   struct stat s;
561   fileinfo fi;
562   uint32_t flags = 0;
563   uint32_t b;
564   uint32_t header_size = 0, segment_size = 0, max_segment = 0, bs;
565   if (!path || !*path) {
566     ERR(GRN_INVALID_ARGUMENT, "[io][open] path is missing");
567     return NULL;
568   }
569   if ((strlen(path) > max_path_len)) {
570     int truncate_length = 10;
571     ERR(GRN_INVALID_ARGUMENT,
572         "[io][open] path is too long: "
573         "<%" GRN_FMT_SIZE ">(max: %" GRN_FMT_SIZE "): <%.*s...>",
574         strlen(path),
575         max_path_len,
576         truncate_length,
577         path);
578     return NULL;
579   }
580   {
581     struct _grn_io_header h;
582     int fd;
583     ssize_t read_bytes;
584     grn_open(fd, path, O_RDWR | GRN_OPEN_FLAG_BINARY);
585     if (fd == -1) {
586       ERRNO_ERR("failed to open path: <%s>",
587                 path);
588       return NULL;
589     }
590     if (fstat(fd, &s) == -1) {
591       ERRNO_ERR("[io][open] failed to file status: <%s>",
592                 path);
593       grn_close(fd);
594       return NULL;
595     }
596     if (s.st_size < sizeof(struct _grn_io_header)) {
597       ERR(GRN_INCOMPATIBLE_FILE_FORMAT,
598           "[io][open] file size is too small: "
599           "<%" GRN_FMT_INT64D ">(required: >= %" GRN_FMT_SIZE "): <%s>",
600           (int64_t)(s.st_size),
601           sizeof(struct _grn_io_header),
602           path);
603       grn_close(fd);
604       return NULL;
605     }
606     read_bytes = grn_read(fd, &h, sizeof(struct _grn_io_header));
607     if (read_bytes != sizeof(struct _grn_io_header)) {
608       ERRNO_ERR("[io][open] failed to read header data: "
609                 "<%" GRN_FMT_SSIZE ">(expected: %" GRN_FMT_SSIZE "): <%s>",
610                 read_bytes,
611                 sizeof(struct _grn_io_header),
612                 path);
613       grn_close(fd);
614       return NULL;
615     }
616     if (memcmp(h.idstr, GRN_IO_IDSTR, GRN_IO_IDSTR_LEN) != 0) {
617       ERR(GRN_INCOMPATIBLE_FILE_FORMAT,
618           "failed to open: format ID is different: <%s>: <%.*s>",
619           path,
620           (int)GRN_IO_IDSTR_LEN, GRN_IO_IDSTR);
621       grn_close(fd);
622       return NULL;
623     }
624     header_size = h.header_size;
625     segment_size = h.segment_size;
626     max_segment = h.max_segment;
627     flags = h.flags;
628     grn_close(fd);
629     if (segment_size == 0) {
630       ERR(GRN_INCOMPATIBLE_FILE_FORMAT, "failed to open: segment size is 0");
631       return NULL;
632     }
633   }
634   b = grn_io_compute_base(header_size);
635   bs = grn_io_compute_base_segment(b, segment_size);
636   grn_fileinfo_init(&fi, 1);
637   if (!grn_fileinfo_open(ctx, &fi, path, O_RDWR)) {
638     struct _grn_io_header *header;
639     header = GRN_MMAP(ctx, &grn_gctx, NULL, &(fi.fmo), &fi, 0, b);
640     if (header) {
641       unsigned long file_size;
642       unsigned int max_nfiles;
643       fileinfo *fis;
644 
645       file_size = grn_io_compute_file_size(header->version);
646       max_nfiles = grn_io_compute_max_n_files(segment_size, max_segment,
647                                               bs, file_size);
648       fis = GRN_MALLOCN(fileinfo, max_nfiles);
649       if (!fis) {
650         GRN_MUNMAP(ctx, &grn_gctx, NULL, &(fi.fmo), &fi, header, b);
651         grn_fileinfo_close(ctx, &fi);
652         return NULL;
653       }
654       grn_fileinfo_init(fis, max_nfiles);
655       grn_memcpy(fis, &fi, sizeof(fileinfo));
656       if ((io = GRN_MALLOC(sizeof(grn_io)))) {
657         grn_io_mapinfo *maps = NULL;
658         if ((maps = GRN_CALLOC(sizeof(grn_io_mapinfo) * max_segment))) {
659           grn_strncpy(io->path, PATH_MAX, path, PATH_MAX);
660           io->header = header;
661           io->user_header = (((byte *) header) + IO_HEADER_SIZE);
662           {
663             io->maps = maps;
664             io->base = b;
665             io->base_seg = bs;
666             io->mode = mode;
667             io->fis = fis;
668             io->ainfo = NULL;
669             io->max_map_seg = 0;
670             io->nmaps = 0;
671             io->count = 0;
672             io->flags = header->flags;
673             io->lock = &header->lock;
674             if (!array_init(ctx, io, io->header->n_arrays)) {
675               grn_io_register(ctx, io);
676               return io;
677             }
678           }
679           if (io->maps) { GRN_FREE(io->maps); }
680         }
681         GRN_FREE(io);
682       }
683       GRN_FREE(fis);
684       GRN_MUNMAP(ctx, &grn_gctx, NULL, &(fi.fmo), &fi, header, b);
685     }
686     grn_fileinfo_close(ctx, &fi);
687   }
688   return NULL;
689 }
690 
691 grn_rc
grn_io_close(grn_ctx * ctx,grn_io * io)692 grn_io_close(grn_ctx *ctx, grn_io *io)
693 {
694   uint32_t max_nfiles;
695 
696   max_nfiles = grn_io_max_n_files(io);
697   grn_io_unregister(ctx, io);
698   if (io->ainfo) { GRN_FREE(io->ainfo); }
699   if (io->maps) {
700     int i;
701     uint32_t max_segment;
702     uint32_t segment_size;
703     unsigned long file_size;
704     uint32_t segments_per_file;
705 
706     max_segment = grn_io_max_segment(io);
707     segment_size = io->header->segment_size;
708     file_size = grn_io_compute_file_size(io->header->version);
709     segments_per_file = file_size / segment_size;
710     for (i = 0; i < max_segment; i++) {
711       grn_io_mapinfo *mi;
712       mi = &(io->maps[i]);
713       if (mi->map) {
714         fileinfo *fi = NULL;
715         /* if (atomic_read(mi->nref)) { return STILL_IN_USE ; } */
716         if (io->fis) {
717           uint32_t bseg = i + io->base_seg;
718           uint32_t fno = bseg / segments_per_file;
719           fi = &io->fis[fno];
720         }
721         GRN_MUNMAP(ctx, &grn_gctx, io, &mi->fmo, fi, mi->map, segment_size);
722       }
723     }
724     GRN_FREE(io->maps);
725   }
726   GRN_MUNMAP(ctx, &grn_gctx, io, (io->fis ? &io->fis->fmo : NULL),
727              io->fis, io->header, io->base);
728   if (io->fis) {
729     int i;
730     for (i = 0; i < max_nfiles; i++) {
731       fileinfo *fi = &(io->fis[i]);
732       grn_fileinfo_close(ctx, fi);
733     }
734     GRN_FREE(io->fis);
735   }
736   GRN_FREE(io);
737   return GRN_SUCCESS;
738 }
739 
740 uint32_t
grn_io_base_seg(grn_io * io)741 grn_io_base_seg(grn_io *io)
742 {
743   return io->base_seg;
744 }
745 
746 const char *
grn_io_path(grn_io * io)747 grn_io_path(grn_io *io)
748 {
749   return io->path;
750 }
751 
752 void *
grn_io_header(grn_io * io)753 grn_io_header(grn_io *io)
754 {
755   return io->user_header;
756 }
757 
758 grn_rc
grn_io_set_type(grn_io * io,uint32_t type)759 grn_io_set_type(grn_io *io, uint32_t type)
760 {
761   if (!io || !io->header) {
762     return GRN_INVALID_ARGUMENT;
763   }
764   io->header->type = type;
765   return GRN_SUCCESS;
766 }
767 
768 uint32_t
grn_io_get_type(grn_io * io)769 grn_io_get_type(grn_io *io)
770 {
771   if (!io || !io->header) { return GRN_VOID; }
772   return io->header->type;
773 }
774 
775 inline static void
gen_pathname(const char * path,char * buffer,int fno)776 gen_pathname(const char *path, char *buffer, int fno)
777 {
778   size_t len = strlen(path);
779   grn_memcpy(buffer, path, len);
780   if (fno) {
781     buffer[len] = '.';
782     grn_itoh(fno, buffer + len + 1, 3);
783     buffer[len + 4] = '\0';
784   } else {
785     buffer[len] = '\0';
786   }
787 }
788 
789 static uint32_t
grn_io_n_files(grn_ctx * ctx,grn_io * io)790 grn_io_n_files(grn_ctx *ctx, grn_io *io)
791 {
792   unsigned long file_size;
793   file_size = grn_io_compute_file_size(io->header->version);
794   return ((io->header->curr_size + file_size - 1) / file_size);
795 }
796 
797 grn_rc
grn_io_size(grn_ctx * ctx,grn_io * io,uint64_t * size)798 grn_io_size(grn_ctx *ctx, grn_io *io, uint64_t *size)
799 {
800   int fno;
801   struct stat s;
802   uint64_t tsize = 0;
803   char buffer[PATH_MAX];
804   uint32_t n_files;
805 
806   n_files = grn_io_n_files(ctx, io);
807   for (fno = 0; fno < n_files; fno++) {
808     gen_pathname(io->path, buffer, fno);
809     if (stat(buffer, &s)) {
810       SERR("failed to stat path to compute size: <%s>",
811            buffer);
812     } else {
813       tsize += s.st_size;
814     }
815   }
816   *size = tsize;
817   return GRN_SUCCESS;
818 }
819 
820 grn_rc
grn_io_remove_raw(grn_ctx * ctx,const char * path)821 grn_io_remove_raw(grn_ctx *ctx, const char *path)
822 {
823   grn_rc rc = GRN_SUCCESS;
824   int fno;
825   char buffer[PATH_MAX];
826 
827   if (grn_unlink(path) != 0) {
828     ERRNO_ERR("[io][remove] failed to remove path: <%s>",
829               path);
830     return ctx->rc;
831   }
832   GRN_LOG(ctx, GRN_LOG_INFO, "[io][remove] removed path: <%s>", path);
833 
834   for (fno = 1; ; fno++) {
835     struct stat s;
836     gen_pathname(path, buffer, fno);
837     if (stat(buffer, &s) != 0) {
838       break;
839     }
840     if (grn_unlink(buffer) == 0) {
841       GRN_LOG(ctx, GRN_LOG_INFO,
842               "[io][remove] removed numbered path: <%d>: <%s>", fno, buffer);
843     } else {
844       ERRNO_ERR("[io][remove] failed to remove numbered path: <%d>: <%s>",
845                 fno, buffer);
846       rc = ctx->rc;
847     }
848   }
849   return rc;
850 }
851 
852 grn_rc
grn_io_remove(grn_ctx * ctx,const char * path)853 grn_io_remove(grn_ctx *ctx, const char *path)
854 {
855   struct stat s;
856 
857   if (stat(path, &s) != 0) {
858     SERR("failed to stat: <%s>", path);
859     return ctx->rc;
860   }
861 
862   return grn_io_remove_raw(ctx, path);
863 }
864 
865 grn_rc
grn_io_remove_if_exist(grn_ctx * ctx,const char * path)866 grn_io_remove_if_exist(grn_ctx *ctx, const char *path)
867 {
868   struct stat s;
869   if (stat(path, &s) == 0) {
870     return grn_io_remove_raw(ctx, path);
871   }
872   return GRN_SUCCESS;
873 }
874 
875 grn_rc
grn_io_rename(grn_ctx * ctx,const char * old_name,const char * new_name)876 grn_io_rename(grn_ctx *ctx, const char *old_name, const char *new_name)
877 {
878   struct stat s;
879   if (stat(old_name, &s)) {
880     SERR("failed to stat path to be renamed: <%s>", old_name);
881     return ctx->rc;
882   } else if (rename(old_name, new_name)) {
883     SERR("failed to rename path: <%s> -> <%s>",
884          old_name, new_name);
885     return ctx->rc;
886   } else {
887     int fno;
888     char old_buffer[PATH_MAX];
889     char new_buffer[PATH_MAX];
890     for (fno = 1; ; fno++) {
891       gen_pathname(old_name, old_buffer, fno);
892       if (!stat(old_buffer, &s)) {
893         gen_pathname(new_name, new_buffer, fno);
894         if (rename(old_buffer, new_buffer)) {
895           SERR("failed to rename path: <%s> -> <%s>",
896                old_buffer, new_buffer);
897         }
898       } else {
899         SERR("failed to stat path to be renamed: <%s>",
900              old_buffer);
901         return ctx->rc;
902       }
903     }
904     return GRN_SUCCESS;
905   }
906 }
907 
908 typedef struct {
909   grn_io_ja_ehead head;
910   char body[256];
911 } ja_element;
912 
913 grn_rc
grn_io_read_ja(grn_io * io,grn_ctx * ctx,grn_io_ja_einfo * einfo,uint32_t epos,uint32_t key,uint32_t segment,uint32_t offset,void ** value,uint32_t * value_len)914 grn_io_read_ja(grn_io *io, grn_ctx *ctx, grn_io_ja_einfo *einfo, uint32_t epos,
915                uint32_t key, uint32_t segment, uint32_t offset, void **value,
916                uint32_t *value_len)
917 {
918   uint32_t rest = 0, size = *value_len + sizeof(grn_io_ja_ehead);
919   uint32_t segment_size = io->header->segment_size;
920   unsigned long file_size = grn_io_compute_file_size(io->header->version);
921   uint32_t segments_per_file = file_size / segment_size;
922   uint32_t bseg = segment + io->base_seg;
923   int fno = bseg / segments_per_file;
924   fileinfo *fi = &io->fis[fno];
925   off_t base = fno ? 0 : io->base - (uint64_t)segment_size * io->base_seg;
926   off_t pos = (uint64_t)segment_size * (bseg % segments_per_file) + offset + base;
927   ja_element *v = GRN_MALLOC(size);
928   if (!v) {
929     *value = NULL;
930     *value_len = 0;
931     return GRN_NO_MEMORY_AVAILABLE;
932   }
933   if (pos + size > file_size) {
934     rest = pos + size - file_size;
935     size = file_size - pos;
936   }
937   if (!grn_fileinfo_opened(fi)) {
938     char path[PATH_MAX];
939     gen_pathname(io->path, path, fno);
940     if (grn_fileinfo_open(ctx, fi, path, O_RDWR|O_CREAT)) {
941       *value = NULL;
942       *value_len = 0;
943       GRN_FREE(v);
944       return ctx->rc;
945     }
946   }
947   if (grn_pread(ctx, fi, v, size, pos)) {
948     *value = NULL;
949     *value_len = 0;
950     GRN_FREE(v);
951     return ctx->rc;
952   }
953   if (einfo->pos != epos) {
954     GRN_LOG(ctx, GRN_LOG_WARNING,
955             "einfo pos changed %x => %x", einfo->pos, epos);
956     *value = NULL;
957     *value_len = 0;
958     GRN_FREE(v);
959     return GRN_FILE_CORRUPT;
960   }
961   if (einfo->size != *value_len) {
962     GRN_LOG(ctx, GRN_LOG_WARNING,
963             "einfo size changed %d => %d", einfo->size, *value_len);
964     *value = NULL;
965     *value_len = 0;
966     GRN_FREE(v);
967     return GRN_FILE_CORRUPT;
968   }
969   if (v->head.key != key) {
970     GRN_LOG(ctx, GRN_LOG_ERROR,
971             "ehead key unmatch %x => %x", key, v->head.key);
972     *value = NULL;
973     *value_len = 0;
974     GRN_FREE(v);
975     return GRN_INVALID_FORMAT;
976   }
977   if (v->head.size != *value_len) {
978     GRN_LOG(ctx, GRN_LOG_ERROR,
979             "ehead size unmatch %d => %d", *value_len, v->head.size);
980     *value = NULL;
981     *value_len = 0;
982     GRN_FREE(v);
983     return GRN_INVALID_FORMAT;
984   }
985   if (rest) {
986     byte *vr = (byte *)v + size;
987     do {
988       fi = &io->fis[++fno];
989       if (!grn_fileinfo_opened(fi)) {
990         char path[PATH_MAX];
991         gen_pathname(io->path, path, fno);
992         if (grn_fileinfo_open(ctx, fi, path, O_RDWR|O_CREAT)) {
993           *value = NULL;
994           *value_len = 0;
995           GRN_FREE(v);
996           return ctx->rc;
997         }
998       }
999       size = rest > file_size ? file_size : rest;
1000       if (grn_pread(ctx, fi, vr, size, 0)) {
1001         *value = NULL;
1002         *value_len = 0;
1003         GRN_FREE(v);
1004         return ctx->rc;
1005       }
1006       vr += size;
1007       rest -= size;
1008     } while (rest);
1009   }
1010   *value = v->body;
1011   return GRN_SUCCESS;
1012 }
1013 
1014 grn_rc
grn_io_write_ja(grn_io * io,grn_ctx * ctx,uint32_t key,uint32_t segment,uint32_t offset,void * value,uint32_t value_len)1015 grn_io_write_ja(grn_io *io, grn_ctx *ctx, uint32_t key,
1016                 uint32_t segment, uint32_t offset, void *value,
1017                 uint32_t value_len)
1018 {
1019   grn_rc rc;
1020   uint32_t rest = 0, size = value_len + sizeof(grn_io_ja_ehead);
1021   uint32_t segment_size = io->header->segment_size;
1022   unsigned long file_size = grn_io_compute_file_size(io->header->version);
1023   uint32_t segments_per_file = file_size / segment_size;
1024   uint32_t bseg = segment + io->base_seg;
1025   int fno = bseg / segments_per_file;
1026   fileinfo *fi = &io->fis[fno];
1027   off_t base = fno ? 0 : io->base - (uint64_t)segment_size * io->base_seg;
1028   off_t pos = (uint64_t)segment_size * (bseg % segments_per_file) + offset + base;
1029   if (pos + size > file_size) {
1030     rest = pos + size - file_size;
1031     size = file_size - pos;
1032   }
1033   if (!grn_fileinfo_opened(fi)) {
1034     char path[PATH_MAX];
1035     gen_pathname(io->path, path, fno);
1036     if ((rc = grn_fileinfo_open(ctx, fi, path, O_RDWR|O_CREAT))) { return rc; }
1037   }
1038   if (value_len <= 256) {
1039     ja_element je;
1040     je.head.size = value_len;
1041     je.head.key = key;
1042     grn_memcpy(je.body, value, value_len);
1043     rc = grn_pwrite(ctx, fi, &je, size, pos);
1044   } else {
1045     grn_io_ja_ehead eh;
1046     eh.size = value_len;
1047     eh.key =  key;
1048     if ((rc = grn_pwrite(ctx, fi, &eh, sizeof(grn_io_ja_ehead), pos))) {
1049       return rc;
1050     }
1051     pos += sizeof(grn_io_ja_ehead);
1052     rc = grn_pwrite(ctx, fi, value, size - sizeof(grn_io_ja_ehead), pos);
1053   }
1054   if (rc) { return rc; }
1055   if (rest) {
1056     byte *vr = (byte *)value + size - sizeof(grn_io_ja_ehead);
1057     do {
1058       fi = &io->fis[++fno];
1059       if (!grn_fileinfo_opened(fi)) {
1060         char path[PATH_MAX];
1061         gen_pathname(io->path, path, fno);
1062         if ((rc = grn_fileinfo_open(ctx, fi, path, O_RDWR|O_CREAT))) {
1063           return rc;
1064         }
1065       }
1066       size = rest > file_size ? file_size : rest;
1067       if ((rc = grn_pwrite(ctx, fi, vr, size, 0))) { return rc; }
1068       vr += size;
1069       rest -= size;
1070     } while (rest);
1071   }
1072   return rc;
1073 }
1074 
1075 grn_rc
grn_io_write_ja_ehead(grn_io * io,grn_ctx * ctx,uint32_t key,uint32_t segment,uint32_t offset,uint32_t value_len)1076 grn_io_write_ja_ehead(grn_io *io, grn_ctx *ctx, uint32_t key,
1077                       uint32_t segment, uint32_t offset, uint32_t value_len)
1078 {
1079   grn_rc rc;
1080   uint32_t segment_size = io->header->segment_size;
1081   unsigned long file_size = grn_io_compute_file_size(io->header->version);
1082   uint32_t segments_per_file = file_size / segment_size;
1083   uint32_t bseg = segment + io->base_seg;
1084   int fno = bseg / segments_per_file;
1085   fileinfo *fi = &io->fis[fno];
1086   off_t base = fno ? 0 : io->base - (uint64_t)segment_size + io->base_seg;
1087   off_t pos = (uint64_t)segment_size * (bseg % segments_per_file) + offset + base;
1088   if (!grn_fileinfo_opened(fi)) {
1089     char path[PATH_MAX];
1090     gen_pathname(io->path, path, fno);
1091     if ((rc = grn_fileinfo_open(ctx, fi, path, O_RDWR|O_CREAT))) { return rc; }
1092   }
1093   {
1094     grn_io_ja_ehead eh;
1095     eh.size = value_len;
1096     eh.key =  key;
1097     return grn_pwrite(ctx, fi, &eh, sizeof(grn_io_ja_ehead), pos);
1098   }
1099 }
1100 
1101 void *
grn_io_win_map(grn_io * io,grn_ctx * ctx,grn_io_win * iw,uint32_t segment,uint32_t offset,uint32_t size,grn_io_rw_mode mode)1102 grn_io_win_map(grn_io *io, grn_ctx *ctx, grn_io_win *iw, uint32_t segment,
1103                uint32_t offset, uint32_t size, grn_io_rw_mode mode)
1104 {
1105   uint32_t nseg, segment_size = io->header->segment_size;
1106   if (offset >= segment_size) {
1107     segment += offset / segment_size;
1108     offset = offset % segment_size;
1109   }
1110   nseg = (offset + size + segment_size - 1) / segment_size;
1111   if (!size || !ctx || segment + nseg > io->header->max_segment) {
1112     return NULL;
1113   }
1114   iw->ctx = ctx;
1115   iw->diff = 0;
1116   iw->io = io;
1117   iw->mode = mode;
1118   iw->tiny_p = 0;
1119   iw->segment = segment;
1120   iw->offset = offset;
1121   iw->nseg = nseg;
1122   iw->size = size;
1123   if (nseg == 1) {
1124     byte *addr = NULL;
1125     GRN_IO_SEG_REF(io, segment, addr);
1126     if (!addr) { return NULL; }
1127     iw->cached = 1;
1128     iw->addr = addr + offset;
1129   } else {
1130     if (!(iw->addr = GRN_MALLOC(size))) { return NULL; }
1131     iw->cached = 0;
1132     switch (mode) {
1133     case grn_io_rdonly:
1134     case grn_io_rdwr:
1135       {
1136         byte *p, *q = NULL;
1137         uint32_t s, r;
1138         for (p = iw->addr, r = size; r; p += s, r -= s, segment++, offset = 0) {
1139           GRN_IO_SEG_REF(io, segment, q);
1140           if (!q) {
1141             GRN_FREE(iw->addr);
1142             return NULL;
1143           }
1144           s = (offset + r > segment_size) ? segment_size - offset : r;
1145           grn_memcpy(p, q + offset, s);
1146           GRN_IO_SEG_UNREF(io, segment);
1147         }
1148       }
1149       break;
1150     case grn_io_wronly:
1151       break;
1152     default :
1153       return NULL;
1154     }
1155   }
1156   return iw->addr;
1157 }
1158 
1159 grn_rc
grn_io_win_unmap(grn_io_win * iw)1160 grn_io_win_unmap(grn_io_win *iw)
1161 {
1162   if (!iw || !iw->io ||!iw->ctx) { return GRN_INVALID_ARGUMENT; }
1163   if (iw->cached) {
1164     if (!iw->tiny_p) { GRN_IO_SEG_UNREF(iw->io, iw->segment); }
1165     return GRN_SUCCESS;
1166   }
1167   {
1168     grn_io *io = iw->io;
1169     grn_ctx *ctx = iw->ctx;
1170     switch (iw->mode) {
1171     case grn_io_rdonly:
1172       if (!iw->addr) { return GRN_INVALID_ARGUMENT; }
1173       GRN_FREE(iw->addr);
1174       return GRN_SUCCESS;
1175     case grn_io_rdwr:
1176     case grn_io_wronly:
1177       {
1178         byte *p, *q = NULL;
1179         uint32_t segment_size = io->header->segment_size;
1180         uint32_t s, r, offset = iw->offset, segment = iw->segment;
1181         for (p = iw->addr, r = iw->size; r;
1182              p += s, r -= s, segment++, offset = 0) {
1183           GRN_IO_SEG_REF(io, segment, q);
1184           if (!q) { return GRN_NO_MEMORY_AVAILABLE; }
1185           s = (offset + r > segment_size) ? segment_size - offset : r;
1186           grn_memcpy(q + offset, p, s);
1187           GRN_IO_SEG_UNREF(io, segment);
1188         }
1189       }
1190       GRN_FREE(iw->addr);
1191       return GRN_SUCCESS;
1192     default :
1193       return GRN_INVALID_ARGUMENT;
1194     }
1195   }
1196 }
1197 
1198 #define DO_MAP(io,fmo,fi,pos,size,segno,res) do {\
1199   (res) = GRN_MMAP(ctx, &grn_gctx, (io), (fmo), (fi), (pos), (size));\
1200   if ((res)) {\
1201     uint32_t nmaps;\
1202     if (io->max_map_seg < segno) { io->max_map_seg = segno; }\
1203     GRN_ATOMIC_ADD_EX(&io->nmaps, 1, nmaps);\
1204     {\
1205       uint64_t tail = io->base + (uint64_t)(size) * ((segno) + 1);\
1206       if (tail > io->header->curr_size) { io->header->curr_size = tail; }\
1207     }\
1208   }\
1209 } while (0)
1210 
1211 void
grn_io_seg_map_(grn_ctx * ctx,grn_io * io,uint32_t segno,grn_io_mapinfo * info)1212 grn_io_seg_map_(grn_ctx *ctx, grn_io *io, uint32_t segno, grn_io_mapinfo *info)
1213 {
1214   uint32_t segment_size = io->header->segment_size;
1215   if ((io->flags & GRN_IO_TEMPORARY)) {
1216     DO_MAP(io, &info->fmo, NULL, 0, segment_size, segno, info->map);
1217   } else {
1218     unsigned long file_size = grn_io_compute_file_size(io->header->version);
1219     uint32_t segments_per_file = file_size / segment_size;
1220     uint32_t bseg = segno + io->base_seg;
1221     uint32_t fno = bseg / segments_per_file;
1222     off_t base = fno ? 0 : io->base - (uint64_t)segment_size * io->base_seg;
1223     off_t pos = (uint64_t)segment_size * (bseg % segments_per_file) + base;
1224     fileinfo *fi = &io->fis[fno];
1225     if (!grn_fileinfo_opened(fi)) {
1226       char path[PATH_MAX];
1227       grn_bool path_exist = GRN_TRUE;
1228       gen_pathname(io->path, path, fno);
1229       path_exist = grn_path_exist(path);
1230       if (!grn_fileinfo_open(ctx, fi, path, O_RDWR|O_CREAT)) {
1231         DO_MAP(io, &info->fmo, fi, pos, segment_size, segno, info->map);
1232         if (!info->map && !path_exist) {
1233           if (grn_unlink(path) == 0) {
1234             GRN_LOG(ctx, GRN_LOG_INFO,
1235                     "[io][map][error] memory mapping is failed and then "
1236                     "removed created map file: <%s>", path);
1237           } else {
1238             ERRNO_ERR("[io][map][error] memory mapping is failed and then "
1239                       "failed to remove created map file: <%s>", path);
1240           }
1241         }
1242       }
1243     } else {
1244       DO_MAP(io, &info->fmo, fi, pos, segment_size, segno, info->map);
1245     }
1246   }
1247 }
1248 
1249 grn_rc
grn_io_seg_expire(grn_ctx * ctx,grn_io * io,uint32_t segno,uint32_t nretry)1250 grn_io_seg_expire(grn_ctx *ctx, grn_io *io, uint32_t segno, uint32_t nretry)
1251 {
1252   uint32_t retry, *pnref;
1253   grn_io_mapinfo *info;
1254   if (!io->maps || segno >= io->header->max_segment) { return GRN_INVALID_ARGUMENT; }
1255   info = &io->maps[segno];
1256   if (!info->map) { return GRN_INVALID_ARGUMENT; }
1257   pnref = &info->nref;
1258   for (retry = 0;; retry++) {
1259     uint32_t nref;
1260     GRN_ATOMIC_ADD_EX(pnref, 1, nref);
1261     if (nref) {
1262       GRN_ATOMIC_ADD_EX(pnref, -1, nref);
1263       if (retry >= GRN_IO_MAX_RETRY) {
1264         GRN_LOG(ctx, GRN_LOG_CRIT,
1265                 "deadlock detected! in grn_io_seg_expire(%p, %u, %u)",
1266                 io, segno, nref);
1267         return GRN_RESOURCE_DEADLOCK_AVOIDED;
1268       }
1269     } else {
1270       GRN_ATOMIC_ADD_EX(pnref, GRN_IO_MAX_REF, nref);
1271       if (nref > 1) {
1272         GRN_ATOMIC_ADD_EX(pnref, -(GRN_IO_MAX_REF + 1), nref);
1273         GRN_FUTEX_WAKE(pnref);
1274         if (retry >= GRN_IO_MAX_RETRY) {
1275           GRN_LOG(ctx, GRN_LOG_CRIT,
1276                   "deadlock detected!! in grn_io_seg_expire(%p, %u, %u)",
1277                   io, segno, nref);
1278           return GRN_RESOURCE_DEADLOCK_AVOIDED;
1279         }
1280       } else {
1281         uint32_t nmaps;
1282         fileinfo *fi = &(io->fis[segno]);
1283         GRN_MUNMAP(ctx, &grn_gctx, io, &info->fmo, fi,
1284                    info->map, io->header->segment_size);
1285         info->map = NULL;
1286         GRN_ATOMIC_ADD_EX(pnref, -(GRN_IO_MAX_REF + 1), nref);
1287         GRN_ATOMIC_ADD_EX(&io->nmaps, -1, nmaps);
1288         GRN_FUTEX_WAKE(pnref);
1289         return GRN_SUCCESS;
1290       }
1291     }
1292     if (retry >= nretry) { return GRN_RESOURCE_DEADLOCK_AVOIDED; }
1293     GRN_FUTEX_WAIT(pnref);
1294   }
1295 }
1296 
1297 uint32_t
grn_io_expire(grn_ctx * ctx,grn_io * io,int count_thresh,uint32_t limit)1298 grn_io_expire(grn_ctx *ctx, grn_io *io, int count_thresh, uint32_t limit)
1299 {
1300   uint32_t m, n = 0, ln = io->nmaps;
1301   switch ((io->flags & (GRN_IO_EXPIRE_GTICK|GRN_IO_EXPIRE_SEGMENT))) {
1302   case GRN_IO_EXPIRE_GTICK :
1303     {
1304       uint32_t nref, nmaps, *pnref = &io->nref;
1305       GRN_ATOMIC_ADD_EX(pnref, 1, nref);
1306       if (!nref && grn_gtick - io->count > count_thresh) {
1307         {
1308           uint32_t i = io->header->n_arrays;
1309           grn_io_array_spec *array_specs = (grn_io_array_spec *)io->user_header;
1310           while (i--) {
1311             memset(io->ainfo[i].addrs, 0,
1312                    sizeof(void *) * array_specs[i].max_n_segments);
1313           }
1314         }
1315         {
1316           uint32_t fno;
1317           for (fno = 0; fno < io->max_map_seg; fno++) {
1318             grn_io_mapinfo *info = &(io->maps[fno]);
1319             if (info->map) {
1320               fileinfo *fi = &(io->fis[fno]);
1321               GRN_MUNMAP(ctx, &grn_gctx, io, &info->fmo, fi,
1322                          info->map, io->header->segment_size);
1323               info->map = NULL;
1324               info->nref = 0;
1325               info->count = grn_gtick;
1326               GRN_ATOMIC_ADD_EX(&io->nmaps, -1, nmaps);
1327               n++;
1328             }
1329           }
1330         }
1331       }
1332       GRN_ATOMIC_ADD_EX(pnref, -1, nref);
1333     }
1334     break;
1335   case GRN_IO_EXPIRE_SEGMENT :
1336     for (m = io->max_map_seg; n < limit && m; m--) {
1337       if (!grn_io_seg_expire(ctx, io, m, 0)) { n++; }
1338     }
1339     break;
1340   case (GRN_IO_EXPIRE_GTICK|GRN_IO_EXPIRE_SEGMENT) :
1341     {
1342       grn_io_mapinfo *info = io->maps;
1343       for (m = io->max_map_seg; n < limit && m; info++, m--) {
1344         if (info->map && (grn_gtick - info->count) > count_thresh) {
1345           uint32_t nmaps, nref, *pnref = &info->nref;
1346           GRN_ATOMIC_ADD_EX(pnref, 1, nref);
1347           if (!nref && info->map && (grn_gtick - info->count) > count_thresh) {
1348             GRN_MUNMAP(ctx, &grn_gctx, io, &info->fmo, NULL,
1349                        info->map, io->header->segment_size);
1350             GRN_ATOMIC_ADD_EX(&io->nmaps, -1, nmaps);
1351             info->map = NULL;
1352             info->count = grn_gtick;
1353             n++;
1354           }
1355           GRN_ATOMIC_ADD_EX(pnref, -1, nref);
1356         }
1357       }
1358     }
1359     break;
1360   }
1361   if (n) {
1362     GRN_LOG(ctx, GRN_LOG_DEBUG, "<%p:%x> expired i=%p max=%d (%d/%d)",
1363             ctx, grn_gtick, io, io->max_map_seg, n, ln);
1364   }
1365   return n;
1366 }
1367 
1368 void *
grn_io_anon_map(grn_ctx * ctx,grn_io_mapinfo * mi,size_t length)1369 grn_io_anon_map(grn_ctx *ctx, grn_io_mapinfo *mi, size_t length)
1370 {
1371   return (mi->map = GRN_MMAP(ctx, ctx, NULL, &mi->fmo, NULL, 0, length));
1372 }
1373 
1374 void
grn_io_anon_unmap(grn_ctx * ctx,grn_io_mapinfo * mi,size_t length)1375 grn_io_anon_unmap(grn_ctx *ctx, grn_io_mapinfo *mi, size_t length)
1376 {
1377   GRN_MUNMAP(ctx, ctx, NULL, &mi->fmo, NULL, mi->map, length);
1378 }
1379 
1380 grn_rc
grn_io_lock(grn_ctx * ctx,grn_io * io,int timeout)1381 grn_io_lock(grn_ctx *ctx, grn_io *io, int timeout)
1382 {
1383   static int _ncalls = 0, _ncolls = 0;
1384   uint32_t count, count_log_border = 1000;
1385   uint32_t rc_check_interval = 1000;
1386   _ncalls++;
1387   if (!io) { return GRN_INVALID_ARGUMENT; }
1388   for (count = 0;; count++) {
1389     uint32_t lock;
1390     GRN_ATOMIC_ADD_EX(io->lock, 1, lock);
1391     if (lock) {
1392       GRN_ATOMIC_ADD_EX(io->lock, -1, lock);
1393       if (count == count_log_border) {
1394         GRN_LOG(ctx, GRN_LOG_NOTICE,
1395                 "io(%s) collisions(%d/%d): lock failed %d times",
1396                 io->path, _ncolls, _ncalls, count_log_border);
1397       }
1398       if (!timeout || (timeout > 0 && timeout == count)) {
1399         GRN_LOG(ctx, GRN_LOG_WARNING,
1400                 "[DB Locked] time out(%d): io(%s) collisions(%d/%d)",
1401                 timeout, io->path, _ncolls, _ncalls);
1402         break;
1403       }
1404       if (!(++_ncolls % 1000000) && (_ncolls > _ncalls)) {
1405         if (_ncolls < 0 || _ncalls < 0) {
1406           _ncolls = 0; _ncalls = 0;
1407         } else {
1408           GRN_LOG(ctx, GRN_LOG_NOTICE,
1409                   "io(%s) collisions(%d/%d)", io->path, _ncolls, _ncalls);
1410         }
1411       }
1412       if ((count % rc_check_interval) == 0) {
1413         if (ctx->rc != GRN_SUCCESS) {
1414           return ctx->rc;
1415         }
1416       }
1417       grn_nanosleep(GRN_LOCK_WAIT_TIME_NANOSECOND);
1418       continue;
1419     }
1420     return GRN_SUCCESS;
1421   }
1422   ERR(GRN_RESOURCE_DEADLOCK_AVOIDED, "grn_io_lock failed");
1423   return ctx->rc;
1424 }
1425 
1426 void
grn_io_unlock(grn_io * io)1427 grn_io_unlock(grn_io *io)
1428 {
1429   if (io) {
1430     uint32_t lock;
1431     GRN_ATOMIC_ADD_EX(io->lock, -1, lock);
1432   }
1433 }
1434 
1435 void
grn_io_clear_lock(grn_io * io)1436 grn_io_clear_lock(grn_io *io)
1437 {
1438   if (io) { *io->lock = 0; }
1439 }
1440 
1441 uint32_t
grn_io_is_locked(grn_io * io)1442 grn_io_is_locked(grn_io *io)
1443 {
1444   return io ? *io->lock : 0;
1445 }
1446 
1447 grn_rc
grn_io_flush(grn_ctx * ctx,grn_io * io)1448 grn_io_flush(grn_ctx *ctx, grn_io *io)
1449 {
1450   grn_rc rc = GRN_SUCCESS;
1451   struct _grn_io_header *header;
1452   uint32_t aligned_header_size;
1453 
1454   if (io->path[0] == '\0') {
1455     return GRN_SUCCESS;
1456   }
1457 
1458   header = io->header;
1459   aligned_header_size = grn_io_compute_base(header->header_size);
1460 
1461   if (GRN_MSYNC(ctx, io->fis[0].fh, header, aligned_header_size) != 0) {
1462     return ctx->rc;
1463   }
1464 
1465   if (io->maps) {
1466     uint32_t i;
1467     uint32_t max_mapped_segment;
1468     uint32_t segment_size;
1469 
1470     max_mapped_segment = grn_io_max_segment(io);
1471     segment_size = header->segment_size;
1472     for (i = 0; i < max_mapped_segment; i++) {
1473       grn_io_mapinfo *info = &(io->maps[i]);
1474       uint32_t nth_file_info;
1475       uint32_t *pnref;
1476       uint32_t nref;
1477       int msync_result;
1478 
1479       if (!info) {
1480         continue;
1481       }
1482 
1483       pnref = &info->nref;
1484       GRN_ATOMIC_ADD_EX(pnref, 1, nref);
1485       if (nref != 0) {
1486         GRN_ATOMIC_ADD_EX(pnref, -1, nref);
1487         continue;
1488       }
1489 
1490       if (!info->map) {
1491         GRN_ATOMIC_ADD_EX(pnref, -1, nref);
1492         GRN_FUTEX_WAKE(pnref);
1493         continue;
1494       }
1495 
1496       nth_file_info = grn_io_compute_nth_file_info(io, i);
1497       msync_result = GRN_MSYNC(ctx,
1498                                io->fis[nth_file_info].fh,
1499                                info->map,
1500                                segment_size);
1501       GRN_ATOMIC_ADD_EX(pnref, -1, nref);
1502       GRN_FUTEX_WAKE(pnref);
1503 
1504       if (msync_result != 0) {
1505         rc = ctx->rc;
1506         break;
1507       }
1508     }
1509   }
1510 
1511   return rc;
1512 }
1513 
1514 grn_bool
grn_io_is_corrupt(grn_ctx * ctx,grn_io * io)1515 grn_io_is_corrupt(grn_ctx *ctx, grn_io *io)
1516 {
1517   uint32_t i;
1518   uint32_t n_files;
1519 
1520   if (!io) {
1521     return GRN_FALSE;
1522   }
1523 
1524   n_files = grn_io_n_files(ctx, io);
1525   for (i = 0; i < n_files; i++) {
1526     char path[PATH_MAX];
1527     struct stat s;
1528     gen_pathname(io->path, path, i);
1529     if (stat(path, &s) != 0) {
1530       SERR("[io][corrupt] used path doesn't exist: <%s>",
1531            path);
1532       return GRN_TRUE;
1533     }
1534   }
1535 
1536   return GRN_FALSE;
1537 }
1538 
1539 size_t
grn_io_get_disk_usage(grn_ctx * ctx,grn_io * io)1540 grn_io_get_disk_usage(grn_ctx *ctx, grn_io *io)
1541 {
1542   size_t usage = 0;
1543   uint32_t i;
1544   uint32_t n_files;
1545 
1546   if (!io) {
1547     return usage;
1548   }
1549 
1550   n_files = grn_io_n_files(ctx, io);
1551   for (i = 0; i < n_files; i++) {
1552     char path[PATH_MAX];
1553     struct stat s;
1554     gen_pathname(io->path, path, i);
1555     if (stat(path, &s) != 0) {
1556       continue;
1557     }
1558     usage += s.st_size;
1559   }
1560 
1561   return usage;
1562 }
1563 
1564 /** mmap abstraction **/
1565 
1566 static size_t mmap_size = 0;
1567 
1568 #ifdef WIN32
1569 
1570 inline static grn_rc
grn_fileinfo_open_v1(grn_ctx * ctx,fileinfo * fi,const char * path,int flags)1571 grn_fileinfo_open_v1(grn_ctx *ctx, fileinfo *fi, const char *path, int flags)
1572 {
1573   CRITICAL_SECTION_INIT(fi->cs);
1574   return GRN_SUCCESS;
1575 }
1576 
1577 inline static void *
grn_mmap_v1(grn_ctx * ctx,grn_ctx * owner_ctx,HANDLE * fmo,fileinfo * fi,off_t offset,size_t length)1578 grn_mmap_v1(grn_ctx *ctx, grn_ctx *owner_ctx, HANDLE *fmo, fileinfo *fi,
1579             off_t offset, size_t length)
1580 {
1581   void *res;
1582   if (!fi) {
1583     if (fmo) {
1584       *fmo = NULL;
1585     }
1586     /* TODO: Try to support VirtualAlloc() as anonymous mmap in POSIX.
1587      * If VirtualAlloc() provides better performance rather than malloc(),
1588      * we'll use it.
1589      */
1590     return GRN_CALLOC(length);
1591   }
1592   /* CRITICAL_SECTION_ENTER(fi->cs); */
1593   /* try to create fmo */
1594   *fmo = CreateFileMapping(fi->fh, NULL, PAGE_READWRITE, 0, offset + length, NULL);
1595   if (!*fmo) {
1596     SERR("CreateFileMapping(%lu + %" GRN_FMT_SIZE ") failed "
1597          "<%" GRN_FMT_SIZE ">",
1598          (DWORD)offset, length,
1599          mmap_size);
1600     return NULL;
1601   }
1602   res = MapViewOfFile(*fmo, FILE_MAP_WRITE, 0, (DWORD)offset, (SIZE_T)length);
1603   if (!res) {
1604     SERR("MapViewOfFile(%lu,%" GRN_FMT_SIZE ") failed <%" GRN_FMT_SIZE ">",
1605          (DWORD)offset, length, mmap_size);
1606     return NULL;
1607   }
1608   /* CRITICAL_SECTION_LEAVE(fi->cs); */
1609   mmap_size += length;
1610   return res;
1611 }
1612 
1613 inline static int
grn_munmap_v1(grn_ctx * ctx,grn_ctx * owner_ctx,HANDLE * fmo,fileinfo * fi,void * start,size_t length)1614 grn_munmap_v1(grn_ctx *ctx, grn_ctx *owner_ctx, HANDLE *fmo, fileinfo *fi,
1615               void *start, size_t length)
1616 {
1617   int r = 0;
1618 
1619   if (!fi) {
1620     GRN_FREE(start);
1621     return r;
1622   }
1623 
1624   if (!fmo) {
1625     GRN_FREE(start);
1626     return r;
1627   }
1628 
1629   if (*fmo) {
1630     if (UnmapViewOfFile(start)) {
1631       mmap_size -= length;
1632     } else {
1633       SERR("UnmapViewOfFile(%p,%" GRN_FMT_SIZE ") failed <%" GRN_FMT_SIZE ">",
1634            start, length, mmap_size);
1635       r = -1;
1636     }
1637     if (!CloseHandle(*fmo)) {
1638       SERR("CloseHandle(%p,%" GRN_FMT_SIZE ") failed <%" GRN_FMT_SIZE ">",
1639            start, length, mmap_size);
1640     }
1641     *fmo = NULL;
1642   } else {
1643     GRN_FREE(start);
1644   }
1645 
1646   return r;
1647 }
1648 
1649 inline static grn_rc
grn_fileinfo_open_v0(grn_ctx * ctx,fileinfo * fi,const char * path,int flags)1650 grn_fileinfo_open_v0(grn_ctx *ctx, fileinfo *fi, const char *path, int flags)
1651 {
1652   /* signature may be wrong.. */
1653   fi->fmo = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, NULL);
1654   /* open failed */
1655   if (fi->fmo == NULL) {
1656     // flock
1657     /* retry to open */
1658     fi->fmo = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, NULL);
1659     /* failed again */
1660     if (fi->fmo == NULL) {
1661       /* try to create fmo */
1662       fi->fmo = CreateFileMapping(fi->fh, NULL, PAGE_READWRITE, 0,
1663                                   GRN_IO_FILE_SIZE_V0, NULL);
1664     }
1665     // funlock
1666   }
1667   if (fi->fmo != NULL) {
1668     if (GetLastError() != ERROR_ALREADY_EXISTS) {
1669       CRITICAL_SECTION_INIT(fi->cs);
1670       return GRN_SUCCESS;
1671     } else {
1672       GRN_LOG(ctx, GRN_LOG_ERROR,
1673               "fmo object already exists! handle=%p", fi->fh);
1674       CloseHandle(fi->fmo);
1675     }
1676   } else {
1677     GRN_LOG(ctx, GRN_LOG_ALERT,
1678             "failed to get FileMappingObject #%lu", GetLastError());
1679   }
1680   CloseHandle(fi->fh);
1681   SERR("OpenFileMapping");
1682   return ctx->rc;
1683 }
1684 
1685 inline static void *
grn_mmap_v0(grn_ctx * ctx,grn_ctx * owner_ctx,fileinfo * fi,off_t offset,size_t length)1686 grn_mmap_v0(grn_ctx *ctx, grn_ctx *owner_ctx, fileinfo *fi, off_t offset,
1687             size_t length)
1688 {
1689   void *res;
1690   if (!fi) { return GRN_CALLOC(length); }
1691   /* file must be exceeded to GRN_IO_FILE_SIZE_V0 when FileMappingObject created.
1692      and, after fmo created, it's not allowed to expand the size of file.
1693   DWORD tail = (DWORD)(offset + length);
1694   DWORD filesize = GetFileSize(fi->fh, NULL);
1695   if (filesize < tail) {
1696     if (SetFilePointer(fi->fh, tail, NULL, FILE_BEGIN) != tail) {
1697       grn_log("SetFilePointer failed");
1698       return NULL;
1699     }
1700     if (!SetEndOfFile(fi->fh)) {
1701       grn_log("SetEndOfFile failed");
1702       return NULL;
1703     }
1704     filesize = tail;
1705   }
1706   */
1707   res = MapViewOfFile(fi->fmo, FILE_MAP_WRITE, 0, (DWORD)offset, (SIZE_T)length);
1708   if (!res) {
1709     MERR("MapViewOfFile failed: <%" GRN_FMT_SIZE ">: %s",
1710          mmap_size, grn_current_error_message());
1711     return NULL;
1712   }
1713   mmap_size += length;
1714   return res;
1715 }
1716 
1717 inline static int
grn_munmap_v0(grn_ctx * ctx,grn_ctx * owner_ctx,fileinfo * fi,void * start,size_t length)1718 grn_munmap_v0(grn_ctx *ctx, grn_ctx *owner_ctx, fileinfo *fi, void *start,
1719               size_t length)
1720 {
1721   if (!fi) {
1722     GRN_FREE(start);
1723     return 0;
1724   }
1725 
1726   if (UnmapViewOfFile(start)) {
1727     mmap_size -= length;
1728     return 0;
1729   } else {
1730     SERR("UnmapViewOfFile(%p,%" GRN_FMT_SIZE ") failed <%" GRN_FMT_SIZE ">",
1731          start, length, mmap_size);
1732     return -1;
1733   }
1734 }
1735 
1736 inline static grn_rc
grn_fileinfo_open_common(grn_ctx * ctx,fileinfo * fi,const char * path,int flags)1737 grn_fileinfo_open_common(grn_ctx *ctx, fileinfo *fi, const char *path, int flags)
1738 {
1739   /* may be wrong if flags is just only O_RDWR */
1740   if ((flags & O_CREAT)) {
1741     DWORD dwCreationDisposition;
1742     const char *flags_description;
1743     if (flags & O_EXCL) {
1744       dwCreationDisposition = CREATE_NEW;
1745       flags_description = "O_RDWR|O_CREAT|O_EXCL";
1746     } else {
1747       dwCreationDisposition = OPEN_ALWAYS;
1748       flags_description = "O_RDWR|O_CREAT";
1749     }
1750     fi->fh = CreateFile(path, GRN_IO_FILE_CREATE_MODE,
1751                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1752                         NULL,
1753                         dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, 0);
1754     if (fi->fh == INVALID_HANDLE_VALUE) {
1755       SERR("CreateFile(<%s>, <%s>) failed",
1756            path, flags_description);
1757       goto exit;
1758     }
1759 
1760     switch (dwCreationDisposition) {
1761     case CREATE_NEW :
1762       GRN_LOG(ctx, GRN_LOG_INFO,
1763               "[io][open] create new file: <%s>", path);
1764       break;
1765     case OPEN_ALWAYS :
1766       if (GetLastError() == ERROR_ALREADY_EXISTS) {
1767         GRN_LOG(ctx, GRN_LOG_INFO,
1768                 "[io][open] open existing file because it exists: <%s>", path);
1769       } else {
1770         GRN_LOG(ctx, GRN_LOG_INFO,
1771                 "[io][open] create new file because it doesn't exist: <%s>",
1772                 path);
1773       }
1774       break;
1775     default :
1776       break;
1777     }
1778 
1779     if (grn_io_use_sparse) {
1780       FILE_SET_SPARSE_BUFFER buffer;
1781       buffer.SetSparse = TRUE;
1782       DWORD returned_bytes;
1783       if (!DeviceIoControl(fi->fh,
1784                            FSCTL_SET_SPARSE,
1785                            &buffer,
1786                            sizeof(FILE_SET_SPARSE_BUFFER),
1787                            NULL,
1788                            0,
1789                            &returned_bytes,
1790                            NULL)) {
1791         GRN_LOG(ctx, GRN_LOG_INFO,
1792                 "Tried to make file sparse but failed: "
1793                 "DeviceIoControl(FSCTL_SET_SPARSE): "
1794                 "<%s>: <%s>",
1795                 path, grn_current_error_message());
1796       }
1797     }
1798 
1799     goto exit;
1800   }
1801 
1802   if ((flags & O_TRUNC)) {
1803     CloseHandle(fi->fh);
1804     /* unable to assign OPEN_ALWAYS and TRUNCATE_EXISTING at once */
1805     fi->fh = CreateFile(path, GRN_IO_FILE_CREATE_MODE,
1806                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1807                         NULL,
1808                         TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
1809     if (fi->fh == INVALID_HANDLE_VALUE) {
1810       SERR("CreateFile(<%s>, <O_RDWR|O_TRUNC>) failed",
1811            path);
1812       goto exit;
1813     }
1814     GRN_LOG(ctx, GRN_LOG_INFO,
1815             "[io][open] truncated: <%s>", path);
1816     goto exit;
1817   }
1818   /* O_RDWR only */
1819   fi->fh = CreateFile(path, GRN_IO_FILE_CREATE_MODE,
1820                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1821                       NULL,
1822                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
1823   if (fi->fh == INVALID_HANDLE_VALUE) {
1824     SERR("CreateFile(<%s>, <O_RDWR>) failed",
1825          path);
1826     goto exit;
1827   }
1828   GRN_LOG(ctx, GRN_LOG_INFO,
1829           "[io][open] open existing file: <%s>", path);
1830 
1831 exit :
1832   return ctx->rc;
1833 }
1834 
1835 inline static grn_rc
grn_fileinfo_open(grn_ctx * ctx,fileinfo * fi,const char * path,int flags)1836 grn_fileinfo_open(grn_ctx *ctx, fileinfo *fi, const char *path, int flags)
1837 {
1838   grn_rc rc;
1839   struct _grn_io_header io_header;
1840   LARGE_INTEGER file_size;
1841   int version = grn_io_version_default;
1842 
1843   rc = grn_fileinfo_open_common(ctx, fi, path, flags);
1844   if (rc != GRN_SUCCESS) {
1845     if (fi->fh) {
1846       CloseHandle(fi->fh);
1847       fi->fh = INVALID_HANDLE_VALUE;
1848     }
1849     return rc;
1850   }
1851 
1852   if (GetFileSizeEx(fi->fh, &file_size) && file_size.QuadPart > 0) {
1853     DWORD header_size;
1854     DWORD read_bytes;
1855     header_size = sizeof(struct _grn_io_header);
1856     ReadFile(fi->fh, &io_header, header_size, &read_bytes, NULL);
1857     if (read_bytes == header_size) {
1858       version = io_header.version;
1859     }
1860     SetFilePointer(fi->fh, 0, NULL, FILE_BEGIN);
1861   }
1862 
1863   if (version == 0) {
1864     return grn_fileinfo_open_v0(ctx, fi, path, flags);
1865   } else {
1866     return grn_fileinfo_open_v1(ctx, fi, path, flags);
1867   }
1868 }
1869 
1870 inline static int
grn_guess_io_version(grn_ctx * ctx,grn_io * io,fileinfo * fi)1871 grn_guess_io_version(grn_ctx *ctx, grn_io *io, fileinfo *fi)
1872 {
1873   if (io) {
1874     return io->header->version;
1875   }
1876 
1877   if (fi) {
1878     if (fi->fmo) {
1879       return 0;
1880     } else {
1881       return 1;
1882     }
1883   }
1884 
1885   return grn_io_version_default;
1886 }
1887 
1888 inline static void *
grn_mmap(grn_ctx * ctx,grn_ctx * owner_ctx,grn_io * io,HANDLE * fmo,fileinfo * fi,off_t offset,size_t length)1889 grn_mmap(grn_ctx *ctx, grn_ctx *owner_ctx, grn_io *io, HANDLE *fmo,
1890          fileinfo *fi, off_t offset, size_t length)
1891 {
1892   int version;
1893 
1894   version = grn_guess_io_version(ctx, io, fi);
1895 
1896   if (version == 0) {
1897     return grn_mmap_v0(ctx, owner_ctx, fi, offset, length);
1898   } else {
1899     return grn_mmap_v1(ctx, owner_ctx, fmo, fi, offset, length);
1900   }
1901 }
1902 
1903 inline static int
grn_munmap(grn_ctx * ctx,grn_ctx * owner_ctx,grn_io * io,HANDLE * fmo,fileinfo * fi,void * start,size_t length)1904 grn_munmap(grn_ctx *ctx, grn_ctx *owner_ctx, grn_io *io,
1905            HANDLE *fmo, fileinfo *fi, void *start, size_t length)
1906 {
1907   int version;
1908 
1909   version = grn_guess_io_version(ctx, io, fi);
1910 
1911   if (version == 0) {
1912     return grn_munmap_v0(ctx, owner_ctx, fi, start, length);
1913   } else {
1914     return grn_munmap_v1(ctx, owner_ctx, fmo, fi, start, length);
1915   }
1916 }
1917 
1918 inline static grn_rc
grn_fileinfo_close(grn_ctx * ctx,fileinfo * fi)1919 grn_fileinfo_close(grn_ctx *ctx, fileinfo *fi)
1920 {
1921   if (fi->fmo != NULL) {
1922     CloseHandle(fi->fmo);
1923     fi->fmo = NULL;
1924   }
1925   if (fi->fh != INVALID_HANDLE_VALUE) {
1926     CloseHandle(fi->fh);
1927     CRITICAL_SECTION_FIN(fi->cs);
1928     fi->fh = INVALID_HANDLE_VALUE;
1929   }
1930   return GRN_SUCCESS;
1931 }
1932 
1933 inline static void
grn_fileinfo_init(fileinfo * fis,int nfis)1934 grn_fileinfo_init(fileinfo *fis, int nfis)
1935 {
1936   for (; nfis--; fis++) {
1937     fis->fh = INVALID_HANDLE_VALUE;
1938     fis->fmo = NULL;
1939   }
1940 }
1941 
1942 inline static int
grn_fileinfo_opened(fileinfo * fi)1943 grn_fileinfo_opened(fileinfo *fi)
1944 {
1945   return fi->fh != INVALID_HANDLE_VALUE;
1946 }
1947 
1948 inline static int
grn_msync(grn_ctx * ctx,HANDLE handle,void * start,size_t length)1949 grn_msync(grn_ctx *ctx, HANDLE handle, void *start, size_t length)
1950 {
1951   BOOL succeeded;
1952   SYSTEMTIME system_time;
1953   FILETIME file_time;
1954 
1955   succeeded = FlushViewOfFile(start, length);
1956   if (!succeeded) {
1957     SERR("FlushViewOfFile(<%p>, <%" GRN_FMT_SIZE ">) failed",
1958          start, length);
1959     return -1;
1960   }
1961 
1962   if (handle == INVALID_HANDLE_VALUE) {
1963     return 0;
1964   }
1965 
1966   GetSystemTime(&system_time);
1967   succeeded = SystemTimeToFileTime(&system_time, &file_time);
1968   if (!succeeded) {
1969     SERR("SystemTimeToFileTime(<%04u-%02u-%02uT%02u:%02u:%02u.%03u>) failed",
1970          system_time.wYear,
1971          system_time.wMonth,
1972          system_time.wDay,
1973          system_time.wHour,
1974          system_time.wMinute,
1975          system_time.wSecond,
1976          system_time.wMilliseconds);
1977     return -1;
1978   }
1979 
1980   succeeded = SetFileTime(handle, NULL, NULL, &file_time);
1981   if (!succeeded) {
1982     SERR("SetFileTime(<%p>, <%p>, <%" GRN_FMT_SIZE ">) failed",
1983          handle, start, length);
1984     return -1;
1985   }
1986 
1987   return 0;
1988 }
1989 
1990 inline static grn_rc
grn_pread(grn_ctx * ctx,fileinfo * fi,void * buf,size_t count,off_t offset)1991 grn_pread(grn_ctx *ctx, fileinfo *fi, void *buf, size_t count, off_t offset)
1992 {
1993   DWORD r, len;
1994   CRITICAL_SECTION_ENTER(fi->cs);
1995   r = SetFilePointer(fi->fh, offset, NULL, FILE_BEGIN);
1996   if (r == INVALID_SET_FILE_POINTER) {
1997     SERR("SetFilePointer");
1998   } else {
1999     if (!ReadFile(fi->fh, buf, (DWORD)count, &len, NULL)) {
2000       SERR("ReadFile");
2001     } else if (len != count) {
2002       /* todo : should retry ? */
2003       ERR(GRN_INPUT_OUTPUT_ERROR,
2004           "ReadFile %" GRN_FMT_SIZE " != %lu",
2005           count, len);
2006     }
2007   }
2008   CRITICAL_SECTION_LEAVE(fi->cs);
2009   return ctx->rc;
2010 }
2011 
2012 inline static grn_rc
grn_pwrite(grn_ctx * ctx,fileinfo * fi,void * buf,size_t count,off_t offset)2013 grn_pwrite(grn_ctx *ctx, fileinfo *fi, void *buf, size_t count, off_t offset)
2014 {
2015   DWORD r, len;
2016   CRITICAL_SECTION_ENTER(fi->cs);
2017   r = SetFilePointer(fi->fh, offset, NULL, FILE_BEGIN);
2018   if (r == INVALID_SET_FILE_POINTER) {
2019     SERR("SetFilePointer");
2020   } else {
2021     if (!WriteFile(fi->fh, buf, (DWORD)count, &len, NULL)) {
2022       SERR("WriteFile");
2023     } else if (len != count) {
2024       /* todo : should retry ? */
2025       ERR(GRN_INPUT_OUTPUT_ERROR,
2026           "WriteFile %" GRN_FMT_SIZE " != %lu",
2027           count, len);
2028     }
2029   }
2030   CRITICAL_SECTION_LEAVE(fi->cs);
2031   return ctx->rc;
2032 }
2033 
2034 #else /* WIN32 */
2035 
2036 inline static grn_rc
grn_fileinfo_open(grn_ctx * ctx,fileinfo * fi,const char * path,int flags)2037 grn_fileinfo_open(grn_ctx *ctx, fileinfo *fi, const char *path, int flags)
2038 {
2039   struct stat st;
2040   grn_open(fi->fd, path, flags);
2041   if (fi->fd == -1) {
2042     ERRNO_ERR("failed to open file info path: <%s>",
2043               path);
2044     return ctx->rc;
2045   }
2046   if (fstat(fi->fd, &st) == -1) {
2047     ERRNO_ERR("failed to stat file info path: <%s>",
2048               path);
2049     return ctx->rc;
2050   }
2051   fi->dev = st.st_dev;
2052   fi->inode = st.st_ino;
2053   return GRN_SUCCESS;
2054 }
2055 
2056 inline static void
grn_fileinfo_init(fileinfo * fis,int nfis)2057 grn_fileinfo_init(fileinfo *fis, int nfis)
2058 {
2059   for (; nfis--; fis++) { fis->fd = -1; }
2060 }
2061 
2062 inline static int
grn_fileinfo_opened(fileinfo * fi)2063 grn_fileinfo_opened(fileinfo *fi)
2064 {
2065   return fi->fd != -1;
2066 }
2067 
2068 inline static grn_rc
grn_fileinfo_close(grn_ctx * ctx,fileinfo * fi)2069 grn_fileinfo_close(grn_ctx *ctx, fileinfo *fi)
2070 {
2071   if (fi->fd != -1) {
2072     if (grn_close(fi->fd) == -1) {
2073       SERR("close");
2074       return ctx->rc;
2075     }
2076     fi->fd = -1;
2077   }
2078   return GRN_SUCCESS;
2079 }
2080 
2081 #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
2082 #define MAP_ANONYMOUS MAP_ANON
2083 #endif
2084 
2085 #include <sys/mman.h>
2086 
2087 inline static void *
grn_mmap(grn_ctx * ctx,grn_ctx * owner_ctx,grn_io * io,fileinfo * fi,off_t offset,size_t length)2088 grn_mmap(grn_ctx *ctx, grn_ctx *owner_ctx, grn_io *io, fileinfo *fi,
2089          off_t offset, size_t length)
2090 {
2091   void *res;
2092   int fd, flags;
2093   if (fi) {
2094     struct stat s;
2095     off_t tail = offset + length;
2096     fd = fi->fd;
2097     if ((fstat(fd, &s) == -1) || (s.st_size < tail && ftruncate(fd, tail) == -1)) {
2098       SERR("fstat");
2099       return NULL;
2100     }
2101     flags = MAP_SHARED;
2102   } else {
2103     fd = -1;
2104     flags = MAP_PRIVATE|MAP_ANONYMOUS;
2105   }
2106   res = mmap(NULL, length, PROT_READ|PROT_WRITE, flags, fd, offset);
2107   if (MAP_FAILED == res) {
2108     MERR("mmap(%" GRN_FMT_LLU ",%d,%" GRN_FMT_LLD ")=%s <%" GRN_FMT_LLU ">",
2109          (unsigned long long int)length, fd, (long long int)offset,
2110          strerror(errno), (unsigned long long int)mmap_size);
2111     return NULL;
2112   }
2113   mmap_size += length;
2114   return res;
2115 }
2116 
2117 #ifdef USE_FAIL_MALLOC
2118 inline static void *
grn_fail_mmap(grn_ctx * ctx,grn_ctx * owner_ctx,grn_io * io,fileinfo * fi,off_t offset,size_t length,const char * file,int line,const char * func)2119 grn_fail_mmap(grn_ctx *ctx, grn_ctx *owner_ctx, grn_io *io, fileinfo *fi,
2120               off_t offset, size_t length,
2121               const char* file, int line, const char *func)
2122 {
2123   if (grn_fail_malloc_check(length, file, line, func)) {
2124     return grn_mmap(ctx, io, fi, offset, length);
2125   } else {
2126     MERR("fail_mmap(%" GRN_FMT_SIZE ",%d,%" GRN_FMT_LLU ") "
2127          "(%s:%d@%s) <%" GRN_FMT_SIZE ">",
2128          length,
2129          fi ? fi->fd : 0,
2130          (long long unsigned int)offset,
2131          file,
2132          line,
2133          func,
2134          mmap_size);
2135     return NULL;
2136   }
2137 }
2138 #endif /* USE_FAIL_MALLOC */
2139 
2140 inline static int
grn_msync(grn_ctx * ctx,void * start,size_t length)2141 grn_msync(grn_ctx *ctx, void *start, size_t length)
2142 {
2143   int r = msync(start, length, MS_SYNC);
2144   if (r == -1) { SERR("msync"); }
2145   return r;
2146 }
2147 
2148 inline static int
grn_munmap(grn_ctx * ctx,grn_ctx * owner_ctx,grn_io * io,fileinfo * fi,void * start,size_t length)2149 grn_munmap(grn_ctx *ctx, grn_ctx *owner_ctx, grn_io *io, fileinfo *fi,
2150            void *start, size_t length)
2151 {
2152   int res;
2153   res = munmap(start, length);
2154   if (res) {
2155     SERR("munmap(%p,%" GRN_FMT_LLU ") failed <%" GRN_FMT_LLU ">",
2156          start,
2157          (unsigned long long int)length,
2158          (unsigned long long int)mmap_size);
2159   } else {
2160     mmap_size -= length;
2161   }
2162   return res;
2163 }
2164 
2165 inline static grn_rc
grn_pread(grn_ctx * ctx,fileinfo * fi,void * buf,size_t count,off_t offset)2166 grn_pread(grn_ctx *ctx, fileinfo *fi, void *buf, size_t count, off_t offset)
2167 {
2168   ssize_t r = pread(fi->fd, buf, count, offset);
2169   if (r != count) {
2170     if (r == -1) {
2171       SERR("pread");
2172     } else {
2173       /* todo : should retry ? */
2174       ERR(GRN_INPUT_OUTPUT_ERROR,
2175           "pread returned %" GRN_FMT_LLD " != %" GRN_FMT_LLU,
2176           (long long int)r, (unsigned long long int)count);
2177     }
2178     return ctx->rc;
2179   }
2180   return GRN_SUCCESS;
2181 }
2182 
2183 inline static grn_rc
grn_pwrite(grn_ctx * ctx,fileinfo * fi,void * buf,size_t count,off_t offset)2184 grn_pwrite(grn_ctx *ctx, fileinfo *fi, void *buf, size_t count, off_t offset)
2185 {
2186   ssize_t r = pwrite(fi->fd, buf, count, offset);
2187   if (r != count) {
2188     if (r == -1) {
2189       SERR("pwrite");
2190     } else {
2191       /* todo : should retry ? */
2192       ERR(GRN_INPUT_OUTPUT_ERROR,
2193           "pwrite returned %" GRN_FMT_LLD " != %" GRN_FMT_LLU,
2194           (long long int)r, (unsigned long long int)count);
2195     }
2196     return ctx->rc;
2197   }
2198   return GRN_SUCCESS;
2199 }
2200 
2201 #endif /* WIN32 */
2202