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-1301 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
52typedef 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
66static uint32_t grn_io_version_default = GRN_IO_VERSION_DEFAULT;
67static grn_bool grn_io_use_sparse = GRN_FALSE;
68
69inline static grn_rc grn_fileinfo_open(grn_ctx *ctx, fileinfo *fi,
70 const char *path, int flags);
71inline static void grn_fileinfo_init(fileinfo *fis, int nfis);
72inline static int grn_fileinfo_opened(fileinfo *fi);
73inline static grn_rc grn_fileinfo_close(grn_ctx *ctx, fileinfo *fi);
74#ifdef WIN32
75inline 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);
78inline 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);
81inline 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 */
90inline static void * grn_mmap(grn_ctx *ctx, grn_ctx *owner_ctx,
91 grn_io *io, fileinfo *fi,
92 off_t offset, size_t length);
93inline static int grn_munmap(grn_ctx *ctx, grn_ctx *owner_ctx,
94 grn_io *io, fileinfo *fi,
95 void *start, size_t length);
96inline 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
102inline 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 */
114inline static grn_rc grn_pread(grn_ctx *ctx, fileinfo *fi, void *buf,
115 size_t count, off_t offset);
116inline static grn_rc grn_pwrite(grn_ctx *ctx, fileinfo *fi, void *buf,
117 size_t count, off_t offset);
118
119void
120grn_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
145static inline uint32_t
146grn_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
153static inline uint32_t
154grn_io_compute_base_segment(uint32_t base, uint32_t segment_size)
155{
156 return (base + segment_size - 1) / segment_size;
157}
158
159static uint32_t
160grn_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
168static inline unsigned long
169grn_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
178static inline uint32_t
179grn_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
188static uint32_t
189grn_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
200static inline uint32_t
201grn_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
215static grn_io *
216grn_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
261static void
262grn_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
280static void
281grn_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
299grn_io *
300grn_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
375static grn_rc
376array_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
405static grn_rc
406array_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
422grn_io *
423grn_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
458inline static uint32_t
459segment_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
493void
494grn_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
512void *
513grn_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
520uint32_t
521grn_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
555grn_io *
556grn_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
691grn_rc
692grn_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
740uint32_t
741grn_io_base_seg(grn_io *io)
742{
743 return io->base_seg;
744}
745
746const char *
747grn_io_path(grn_io *io)
748{
749 return io->path;
750}
751
752void *
753grn_io_header(grn_io *io)
754{
755 return io->user_header;
756}
757
758grn_rc
759grn_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
768uint32_t
769grn_io_get_type(grn_io *io)
770{
771 if (!io || !io->header) { return GRN_VOID; }
772 return io->header->type;
773}
774
775inline static void
776gen_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
789static uint32_t
790grn_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
797grn_rc
798grn_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
820grn_rc
821grn_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
852grn_rc
853grn_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
865grn_rc
866grn_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
875grn_rc
876grn_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
908typedef struct {
909 grn_io_ja_ehead head;
910 char body[256];
911} ja_element;
912
913grn_rc
914grn_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
1014grn_rc
1015grn_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
1075grn_rc
1076grn_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
1101void *
1102grn_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
1159grn_rc
1160grn_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
1211void
1212grn_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
1249grn_rc
1250grn_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
1297uint32_t
1298grn_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
1368void *
1369grn_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
1374void
1375grn_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
1380grn_rc
1381grn_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
1426void
1427grn_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
1435void
1436grn_io_clear_lock(grn_io *io)
1437{
1438 if (io) { *io->lock = 0; }
1439}
1440
1441uint32_t
1442grn_io_is_locked(grn_io *io)
1443{
1444 return io ? *io->lock : 0;
1445}
1446
1447grn_rc
1448grn_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
1514grn_bool
1515grn_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
1539size_t
1540grn_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
1566static size_t mmap_size = 0;
1567
1568#ifdef WIN32
1569
1570inline static grn_rc
1571grn_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
1577inline static void *
1578grn_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
1613inline static int
1614grn_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
1649inline static grn_rc
1650grn_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
1685inline static void *
1686grn_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
1717inline static int
1718grn_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
1736inline static grn_rc
1737grn_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
1831exit :
1832 return ctx->rc;
1833}
1834
1835inline static grn_rc
1836grn_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
1870inline static int
1871grn_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
1888inline static void *
1889grn_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
1903inline static int
1904grn_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
1918inline static grn_rc
1919grn_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
1933inline static void
1934grn_fileinfo_init(fileinfo *fis, int nfis)
1935{
1936 for (; nfis--; fis++) {
1937 fis->fh = INVALID_HANDLE_VALUE;
1938 fis->fmo = NULL;
1939 }
1940}
1941
1942inline static int
1943grn_fileinfo_opened(fileinfo *fi)
1944{
1945 return fi->fh != INVALID_HANDLE_VALUE;
1946}
1947
1948inline static int
1949grn_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
1990inline static grn_rc
1991grn_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
2012inline static grn_rc
2013grn_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
2036inline static grn_rc
2037grn_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
2056inline static void
2057grn_fileinfo_init(fileinfo *fis, int nfis)
2058{
2059 for (; nfis--; fis++) { fis->fd = -1; }
2060}
2061
2062inline static int
2063grn_fileinfo_opened(fileinfo *fi)
2064{
2065 return fi->fd != -1;
2066}
2067
2068inline static grn_rc
2069grn_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
2087inline static void *
2088grn_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
2118inline static void *
2119grn_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
2140inline static int
2141grn_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
2148inline static int
2149grn_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
2165inline static grn_rc
2166grn_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
2183inline static grn_rc
2184grn_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