Kouhei Sutou
null+****@clear*****
Mon Feb 8 16:24:59 JST 2016
Kouhei Sutou 2016-02-08 16:24:59 +0900 (Mon, 08 Feb 2016) New Revision: a2397def8defe7a4addf39f68fef529f9cdedc8e https://github.com/groonga/groonga/commit/a2397def8defe7a4addf39f68fef529f9cdedc8e Message: Add missing error report on opening file Modified files: lib/io.c test/command_line/suite/grndb/test_recover.rb Modified: lib/io.c (+53 -15) =================================================================== --- lib/io.c 2016-02-08 14:39:44 +0900 (9913c8a) +++ lib/io.c 2016-02-08 16:24:59 +0900 (6a63521) @@ -518,36 +518,74 @@ grn_io_detect_type(grn_ctx *ctx, const char *path) grn_io * grn_io_open(grn_ctx *ctx, const char *path, grn_io_mode mode) { + size_t max_path_len = PATH_MAX - 4; grn_io *io; struct stat s; fileinfo fi; uint32_t flags = 0; uint32_t b; uint32_t header_size = 0, segment_size = 0, max_segment = 0, bs; - if (!path || !*path || (strlen(path) > PATH_MAX - 4)) { return NULL; } + if (!path || !*path) { + ERR(GRN_INVALID_ARGUMENT, "[io][open] path is missing"); + return NULL; + } + if ((strlen(path) > max_path_len)) { + int truncate_length = 10; + ERR(GRN_INVALID_ARGUMENT, + "[io][open] path is too long: " + "<%" GRN_FMT_SIZE ">(max: %" GRN_FMT_SIZE "): <%.*s...>", + strlen(path), + max_path_len, + truncate_length, + path); + return NULL; + } { struct _grn_io_header h; int fd; + ssize_t read_bytes; grn_open(fd, path, O_RDWR | GRN_OPEN_FLAG_BINARY); if (fd == -1) { ERRNO_ERR("failed to open path: <%s>", path); return NULL; } - if (fstat(fd, &s) != -1 && s.st_size >= sizeof(struct _grn_io_header)) { - if (grn_read(fd, &h, sizeof(struct _grn_io_header)) == sizeof(struct _grn_io_header)) { - if (!memcmp(h.idstr, GRN_IO_IDSTR, GRN_IO_IDSTR_LEN)) { - header_size = h.header_size; - segment_size = h.segment_size; - max_segment = h.max_segment; - flags = h.flags; - } else { - ERR(GRN_INCOMPATIBLE_FILE_FORMAT, - "failed to open: format ID is different: <%s>: <%.*s>", - path, - (int)GRN_IO_IDSTR_LEN, GRN_IO_IDSTR); - } - } + if (fstat(fd, &s) == -1) { + ERRNO_ERR("[io][open] failed to file status: <%s>", + path); + grn_close(fd); + return NULL; + } + if (s.st_size < sizeof(struct _grn_io_header)) { + ERR(GRN_INCOMPATIBLE_FILE_FORMAT, + "[io][open] file size is too small: " + "<%" GRN_FMT_SIZE ">(required: >= %" GRN_FMT_SIZE "): <%s>", + s.st_size, + sizeof(struct _grn_io_header), + path); + grn_close(fd); + return NULL; + } + read_bytes = grn_read(fd, &h, sizeof(struct _grn_io_header)); + if (read_bytes != sizeof(struct _grn_io_header)) { + ERRNO_ERR("[io][open] failed to read header data: " + "<%" GRN_FMT_SSIZE ">(expected: %" GRN_FMT_SSIZE "): <%s>", + read_bytes, + sizeof(struct _grn_io_header), + path); + grn_close(fd); + return NULL; + } + if (!memcmp(h.idstr, GRN_IO_IDSTR, GRN_IO_IDSTR_LEN)) { + header_size = h.header_size; + segment_size = h.segment_size; + max_segment = h.max_segment; + flags = h.flags; + } else { + ERR(GRN_INCOMPATIBLE_FILE_FORMAT, + "failed to open: format ID is different: <%s>: <%.*s>", + path, + (int)GRN_IO_IDSTR_LEN, GRN_IO_IDSTR); } grn_close(fd); if (!segment_size) { return NULL; } Modified: test/command_line/suite/grndb/test_recover.rb (+59 -0) =================================================================== --- test/command_line/suite/grndb/test_recover.rb 2016-02-08 14:39:44 +0900 (06286fc) +++ test/command_line/suite/grndb/test_recover.rb 2016-02-08 16:24:59 +0900 (4302869) @@ -50,4 +50,63 @@ object corrupt: <[db][recover] column may be broken: <Users.age>: please truncat result = grndb("recover") assert_equal("", result.error_output) end + + def test_empty_file + groonga("table_create", "Users", "TABLE_HASH_KEY", "ShortText") + _id, _name, path, *_ = JSON.parse(groonga("table_list").output)[1][1] + FileUtils.rm(path) + FileUtils.touch(path) + error = assert_raise(CommandRunner::Error) do + grndb("recover") + end + assert_equal(<<-MESSAGE, error.error_output) +Failed to recover database: <#{@database_path}> +incompatible file format: <[io][open] file size is too small: <0>(required: >= 64): <#{path[0..68]}>(-65) + MESSAGE + end + + def test_broken_id + groonga("table_create", "Users", "TABLE_HASH_KEY", "ShortText") + _id, _name, path, *_ = JSON.parse(groonga("table_list").output)[1][1] + data = File.binread(path) + data[0] = "X" + File.binwrite(path, data) + error = assert_raise(CommandRunner::Error) do + grndb("recover") + end + assert_equal(<<-MESSAGE, error.error_output) +Failed to recover database: <#{@database_path}> +incompatible file format: <failed to open: format ID is different: <#{path[0..85]}>(-65) + MESSAGE + end + + def test_broken_type_hash + groonga("table_create", "Users", "TABLE_HASH_KEY", "ShortText") + _id, _name, path, *_ = JSON.parse(groonga("table_list").output)[1][1] + data = File.binread(path) + data[16] = "\0" + File.binwrite(path, data) + error = assert_raise(CommandRunner::Error) do + grndb("recover") + end + assert_equal(<<-MESSAGE, error.error_output) +Failed to recover database: <#{@database_path}> +invalid format: <[table][hash] file type must be 0x30: <0000>>(-54) + MESSAGE + end + + def test_broken_type_array + groonga("table_create", "Logs", "TABLE_NO_KEY") + _id, _name, path, *_ = JSON.parse(groonga("table_list").output)[1][1] + data = File.binread(path) + data[16] = "\0" + File.binwrite(path, data) + error = assert_raise(CommandRunner::Error) do + grndb("recover") + end + assert_equal(<<-MESSAGE, error.error_output) +Failed to recover database: <#{@database_path}> +invalid format: <[table][array] file type must be 0x33: <0000>>(-54) + MESSAGE + end end -------------- next part -------------- HTML����������������������������... Download