git: 14598537acec - main - db/hash.c: Allow O_WRONLY in dbm_open
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 30 Jul 2025 09:31:56 UTC
The branch main has been updated by bnovkov:
URL: https://cgit.FreeBSD.org/src/commit/?id=14598537acecb512432e33e001762c912cc25799
commit 14598537acecb512432e33e001762c912cc25799
Author: Bojan Novković <bnovkov@FreeBSD.org>
AuthorDate: 2025-07-25 09:01:52 +0000
Commit: Bojan Novković <bnovkov@FreeBSD.org>
CommitDate: 2025-07-30 09:31:16 +0000
db/hash.c: Allow O_WRONLY in dbm_open
The dbm(3) manpage explicitly states that O_WRONLY is not allowed in
dbm_open, but a more recent comment in ` __hash_open` suggests otherwise.
Furthermore, POSIX.1 allows O_WRONLY in dbm_open and states
that the underlying file must be opened for both reading and writing.
Fix this by correcting the O_WRONLY check and moving it further into
the function to make sure that the original flags are stored in hashp.
Sponsored by: Klara, Inc.
Reviewed by: markj
Differential Revision: https://reviews.freebsd.org/D51514
---
lib/libc/db/hash/hash.c | 15 +++---
lib/libc/db/man/dbm.3 | 5 +-
lib/libc/db/man/dbopen.3 | 5 +-
lib/libc/tests/db/Makefile | 1 +
lib/libc/tests/db/dbm_open_test.c | 23 ++++++---
lib/libc/tests/db/dbm_perm_test.c | 98 +++++++++++++++++++++++++++++++++++++++
6 files changed, 126 insertions(+), 21 deletions(-)
diff --git a/lib/libc/db/hash/hash.c b/lib/libc/db/hash/hash.c
index cc96fb5ce326..1eb01ee0f0c5 100644
--- a/lib/libc/db/hash/hash.c
+++ b/lib/libc/db/hash/hash.c
@@ -99,11 +99,6 @@ __hash_open(const char *file, int flags, int mode,
DB *dbp;
int bpages, hdrsize, new_table, nsegs, save_errno;
- if ((flags & O_ACCMODE) == O_WRONLY) {
- errno = EINVAL;
- return (NULL);
- }
-
if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB))))
return (NULL);
hashp->fp = -1;
@@ -115,6 +110,10 @@ __hash_open(const char *file, int flags, int mode,
* we can check accesses.
*/
hashp->flags = flags;
+ if ((flags & O_ACCMODE) == O_WRONLY) {
+ flags &= ~O_WRONLY;
+ flags |= O_RDWR;
+ }
if (file) {
if ((hashp->fp = _open(file, flags | O_CLOEXEC, mode)) == -1)
@@ -180,7 +179,7 @@ __hash_open(const char *file, int flags, int mode,
__buf_init(hashp, DEF_BUFSIZE);
hashp->new_file = new_table;
- hashp->save_file = file && (hashp->flags & O_RDWR);
+ hashp->save_file = file && (flags & O_RDWR);
hashp->cbucket = -1;
if (!(dbp = (DB *)malloc(sizeof(DB)))) {
save_errno = errno;
@@ -524,6 +523,10 @@ hash_get(const DB *dbp, const DBT *key, DBT *data, u_int32_t flag)
hashp->error = errno = EINVAL;
return (ERROR);
}
+ if ((hashp->flags & O_ACCMODE) == O_WRONLY) {
+ hashp->error = errno = EPERM;
+ return (ERROR);
+ }
return (hash_access(hashp, HASH_GET, (DBT *)key, data));
}
diff --git a/lib/libc/db/man/dbm.3 b/lib/libc/db/man/dbm.3
index c5a83c7acef4..30787600ad2d 100644
--- a/lib/libc/db/man/dbm.3
+++ b/lib/libc/db/man/dbm.3
@@ -13,7 +13,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 2, 2022
+.Dd July 25, 2025
.Dt DBM 3
.Os
.Sh NAME
@@ -99,9 +99,6 @@ is a typical value for
.Li 0660
is a typical value for
.Fa mode .
-.Dv O_WRONLY
-is not allowed in
-.Fa flags .
The pointer returned by
.Fn dbm_open
identifies the database and is the
diff --git a/lib/libc/db/man/dbopen.3 b/lib/libc/db/man/dbopen.3
index 64cef88506d8..7fe515f17849 100644
--- a/lib/libc/db/man/dbopen.3
+++ b/lib/libc/db/man/dbopen.3
@@ -76,13 +76,10 @@ are as specified to the
.Xr open 2
routine, however, only the
.Dv O_CREAT , O_EXCL , O_EXLOCK , O_NOFOLLOW , O_NONBLOCK ,
-.Dv O_RDONLY , O_RDWR , O_SHLOCK , O_SYNC
+.Dv O_RDONLY , O_RDWR , O_SHLOCK , O_SYNC, O_WRONLY,
and
.Dv O_TRUNC
flags are meaningful.
-(Note, opening a database file
-.Dv O_WRONLY
-is not possible.)
.\"Three additional options may be specified by
.\".Em or Ns 'ing
.\"them into the
diff --git a/lib/libc/tests/db/Makefile b/lib/libc/tests/db/Makefile
index 54b38b94a581..cc181cc81160 100644
--- a/lib/libc/tests/db/Makefile
+++ b/lib/libc/tests/db/Makefile
@@ -8,6 +8,7 @@ PROGS+= h_lfsr
${PACKAGE}FILES+= README
ATF_TESTS_C+= dbm_open_test
+ATF_TESTS_C+= dbm_perm_test
NETBSD_ATF_TESTS_C+= db_hash_seq_test
NETBSD_ATF_TESTS_SH+= db_test
diff --git a/lib/libc/tests/db/dbm_open_test.c b/lib/libc/tests/db/dbm_open_test.c
index 18d398e16b2a..8a3e888bf72c 100644
--- a/lib/libc/tests/db/dbm_open_test.c
+++ b/lib/libc/tests/db/dbm_open_test.c
@@ -4,14 +4,15 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
-#include <sys/mman.h>
-
#include <fcntl.h>
#include <ndbm.h>
#include <stdio.h>
#include <atf-c.h>
+static const char *path = "tmp";
+static const char *dbname = "tmp.db";
+
ATF_TC(dbm_open_missing_test);
ATF_TC_HEAD(dbm_open_missing_test, tc)
{
@@ -21,23 +22,31 @@ ATF_TC_HEAD(dbm_open_missing_test, tc)
ATF_TC_BODY(dbm_open_missing_test, tc)
{
- const char *path = "tmp";
- const char *dbname = "tmp.db";
/*
* POSIX.1 specifies that a missing database file should
* always get created if O_CREAT is present, except when
* O_EXCL is specified as well.
*/
- ATF_CHECK(dbm_open(path, O_RDONLY, _PROT_ALL) == NULL);
+ ATF_CHECK(dbm_open(path, O_RDONLY, 0755) == NULL);
+ ATF_REQUIRE(!atf_utils_file_exists(dbname));
+ ATF_CHECK(dbm_open(path, O_RDONLY | O_CREAT, 0755) != NULL);
+ ATF_REQUIRE(atf_utils_file_exists(dbname));
+ ATF_CHECK(dbm_open(path, O_RDONLY | O_CREAT | O_EXCL, 0755) == NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(dbm_open_wronly_test);
+ATF_TC_BODY(dbm_open_wronly_test, tc)
+{
+ ATF_CHECK(dbm_open(path, O_WRONLY, 0755) == NULL);
ATF_REQUIRE(!atf_utils_file_exists(dbname));
- ATF_CHECK(dbm_open(path, O_RDONLY | O_CREAT, _PROT_ALL) != NULL);
+ ATF_CHECK(dbm_open(path, O_WRONLY | O_CREAT, 0755) != NULL);
ATF_REQUIRE(atf_utils_file_exists(dbname));
- ATF_CHECK(dbm_open(path, O_RDONLY | O_CREAT | O_EXCL, _PROT_ALL) == NULL);
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, dbm_open_missing_test);
+ ATF_TP_ADD_TC(tp, dbm_open_wronly_test);
return (atf_no_error());
}
diff --git a/lib/libc/tests/db/dbm_perm_test.c b/lib/libc/tests/db/dbm_perm_test.c
new file mode 100644
index 000000000000..c07210292014
--- /dev/null
+++ b/lib/libc/tests/db/dbm_perm_test.c
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <ndbm.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+static const char *path = "tmp";
+static const char *dbname = "tmp.db";
+
+static void
+create_db(void)
+{
+ DB *db;
+ datum data, key;
+
+ data.dptr = "bar";
+ data.dsize = strlen("bar");
+ key.dptr = "foo";
+ key.dsize = strlen("foo");
+
+ db = dbm_open(path, O_RDWR | O_CREAT, 0755);
+ ATF_CHECK(db != NULL);
+ ATF_REQUIRE(atf_utils_file_exists(dbname));
+ ATF_REQUIRE(dbm_store(db, key, data, DBM_INSERT) != -1);
+ dbm_close(db);
+}
+
+ATF_TC_WITHOUT_HEAD(dbm_rdonly_test);
+ATF_TC_BODY(dbm_rdonly_test, tc)
+{
+ DB *db;
+ datum data, key;
+
+ bzero(&data, sizeof(data));
+ key.dptr = "foo";
+ key.dsize = strlen("foo");
+ create_db();
+
+ db = dbm_open(path, O_RDONLY, 0755);
+ data = dbm_fetch(db, key);
+ ATF_REQUIRE(data.dptr != NULL);
+ ATF_REQUIRE(strncmp((const char*)data.dptr, "bar", data.dsize) == 0);
+ ATF_REQUIRE(dbm_store(db, key, data, DBM_REPLACE) == -1);
+ ATF_REQUIRE(errno == EPERM);
+}
+
+ATF_TC_WITHOUT_HEAD(dbm_wronly_test);
+ATF_TC_BODY(dbm_wronly_test, tc)
+{
+ DB *db;
+ datum data, key;
+
+ key.dptr = "foo";
+ key.dsize = strlen("foo");
+ data.dptr = "baz";
+ data.dsize = strlen("baz");
+ create_db();
+
+ db = dbm_open(path, O_WRONLY, 0755);
+ data = dbm_fetch(db, key);
+ ATF_REQUIRE(data.dptr == NULL);
+ ATF_REQUIRE(errno == EPERM);
+ ATF_REQUIRE(dbm_store(db, key, data, DBM_REPLACE) != -1);
+}
+
+ATF_TC_WITHOUT_HEAD(dbm_rdwr_test);
+ATF_TC_BODY(dbm_rdwr_test, tc)
+{
+ DB *db;
+ datum data, key;
+
+ key.dptr = "foo";
+ key.dsize = strlen("foo");
+ create_db();
+
+ db = dbm_open(path, O_RDWR, 0755);
+ data = dbm_fetch(db, key);
+ ATF_REQUIRE(data.dptr != NULL);
+ data.dptr = "baz";
+ data.dsize = strlen("baz");
+ ATF_REQUIRE(dbm_store(db, key, data, DBM_REPLACE) != -1);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, dbm_rdonly_test);
+ ATF_TP_ADD_TC(tp, dbm_wronly_test);
+ ATF_TP_ADD_TC(tp, dbm_rdwr_test);
+
+ return (atf_no_error());
+}