mirror of
https://git.sr.ht/~cismonx/bookmarkfs
synced 2025-06-07 19:58:50 +00:00
Compare commits
14 commits
Author | SHA1 | Date | |
---|---|---|---|
|
29338ca02b | ||
|
eb426f9fc4 | ||
|
b5fa6960ef | ||
|
6fb9438d3c | ||
|
c1cf9db2a1 | ||
|
495f8592e6 | ||
|
d5eae85774 | ||
|
ab88e0e839 | ||
|
e6809b7e84 | ||
|
5a28b069c4 | ||
|
83f201435f | ||
|
0e20604c73 | ||
|
9c0d5fb337 | ||
|
565063ee9b |
19 changed files with 605 additions and 106 deletions
|
@ -8,7 +8,7 @@ dnl This file is offered as-is, without any warranty.
|
|||
dnl
|
||||
|
||||
AC_PREREQ([2.70])
|
||||
AC_INIT([bookmarkfs], [0.1.0], [bug-bookmarkfs@nongnu.org])
|
||||
AC_INIT([bookmarkfs], [0.1.1], [bug-bookmarkfs@nongnu.org])
|
||||
AC_CONFIG_SRCDIR([bookmarkfs_util.pc.in])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
|
|
@ -32,7 +32,7 @@ bookmarkctl - manage a mounted BookmarkFS filesystem
|
|||
.B bookmarkctl
|
||||
.B xattr\-get
|
||||
.RI [ options ]
|
||||
.B \-m
|
||||
.B \-a
|
||||
.IR attrname "... " pathname
|
||||
.PP
|
||||
.B bookmarkctl
|
||||
|
@ -145,7 +145,7 @@ Treat the value as binary, and print it verbatim.
|
|||
.IP
|
||||
If this option is not provided, non-printable characters are replaced with '?'.
|
||||
.TP
|
||||
.B \-m
|
||||
.B \-a
|
||||
Switch to multi-attrname mode, where multiple extended attribute names
|
||||
can be specified instead of multiple files.
|
||||
.IP
|
||||
|
|
|
@ -630,7 +630,7 @@ Displays extended attribute values.
|
|||
|
||||
@example
|
||||
bookmarkctl xattr-get [@var{options}] @var{attrname} @var{pathname}...
|
||||
bookmarkctl xattr-get [@var{options}] -m @var{attrname}... @var{pathname}
|
||||
bookmarkctl xattr-get [@var{options}] -a @var{attrname}... @var{pathname}
|
||||
@end example
|
||||
|
||||
@table @var
|
||||
|
@ -650,7 +650,7 @@ Treat the value as binary, and print it verbatim.
|
|||
If this option is not provided, non-printable characters are replaced with
|
||||
@samp{?}.
|
||||
|
||||
@item -m
|
||||
@item -a
|
||||
Switch to multi-attrname mode, where multiple extended attribute names
|
||||
can be specified instead of multiple files.
|
||||
|
||||
|
@ -1358,6 +1358,12 @@ The bookmark creation time.
|
|||
|
||||
Value is a decimal integer representing number of microseconds since
|
||||
the Unix epoch.
|
||||
|
||||
@item keyword
|
||||
The keyword associated with the bookmark.
|
||||
@xref{Keywords}.
|
||||
|
||||
This attribute is read-only.
|
||||
@end table
|
||||
|
||||
Notable limitations:
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
#define BM_XATTR_TITLE 2
|
||||
#define BM_XATTR_GUID 3
|
||||
#define BM_XATTR_DATE_ADDED 4
|
||||
#define BM_XATTR_KEYWORD 5
|
||||
#define MOZBM_XATTR_START BM_XATTR_TITLE
|
||||
|
||||
#define BACKEND_EXCLUSIVE_LOCK ( 1u << 16 )
|
||||
|
@ -132,11 +133,14 @@ enum {
|
|||
STMT_MOZBM_MTIME_UPDATE,
|
||||
STMT_MOZBM_POS_SHIFT,
|
||||
STMT_MOZBM_POS_UPDATE,
|
||||
STMT_MOZBM_PURGE,
|
||||
STMT_MOZBM_PURGE_CHECK,
|
||||
STMT_MOZBM_UPDATE,
|
||||
STMT_MOZBMDEL_INSERT,
|
||||
STMT_MOZKW_DELETE,
|
||||
STMT_MOZKW_INSERT,
|
||||
STMT_MOZKW_LOOKUP,
|
||||
STMT_MOZKW_PURGE,
|
||||
STMT_MOZKW_RENAME,
|
||||
STMT_TAG_ENTRY_LOOKUP,
|
||||
#endif /* defined(BOOKMARKFS_BACKEND_FIREFOX_WRITE) */
|
||||
|
@ -287,7 +291,7 @@ static bool is_valid_guid (char const *, size_t);
|
|||
static int keyword_create (struct backend_ctx *, char const *, size_t,
|
||||
struct bookmarkfs_bookmark_stat *);
|
||||
static int mozbm_check_cb (void *, sqlite3_stmt *);
|
||||
static int mozbm_delete (struct backend_ctx *, int64_t, bool);
|
||||
static int mozbm_delete (struct backend_ctx *, int64_t, bool, bool);
|
||||
static int mozbm_delete_cb (void *, sqlite3_stmt *);
|
||||
static int mozbm_get_title (struct backend_ctx *, int64_t, int64_t,
|
||||
db_query_row_func *, void *);
|
||||
|
@ -301,11 +305,14 @@ static int mozbm_mtime_update (struct backend_ctx *, int64_t, int64_t *);
|
|||
static int mozbm_pos_shift (struct backend_ctx *, int64_t, int64_t,
|
||||
int64_t *, enum bookmarkfs_permd_op);
|
||||
static int mozbm_pos_update (struct backend_ctx *, int64_t, int64_t);
|
||||
static int mozbm_purge (struct backend_ctx *, int64_t);
|
||||
static int mozbm_purge_check (struct backend_ctx *, int64_t);
|
||||
static int mozbm_update (struct backend_ctx *, struct mozbm *);
|
||||
static int mozbmdel_insert (struct backend_ctx *, char const *);
|
||||
static int mozkw_delete (struct backend_ctx *, char const *, size_t);
|
||||
static int mozkw_insert (struct backend_ctx *, struct mozkw *);
|
||||
static int mozkw_lookup (struct backend_ctx *, struct mozkw *);
|
||||
static int mozkw_purge (struct backend_ctx *, int64_t);
|
||||
static int mozkw_rename (struct backend_ctx *, char const *,
|
||||
char const *, uint32_t);
|
||||
static int mozorigin_delete (struct backend_ctx *, int64_t);
|
||||
|
@ -317,8 +324,9 @@ static int mozplace_addref (struct backend_ctx *, char const *, size_t,
|
|||
static int mozplace_addref_cb (void *, sqlite3_stmt *);
|
||||
static int mozplace_addref_id (struct backend_ctx *, int64_t);
|
||||
static int mozplace_delete (struct backend_ctx *, int64_t, int64_t);
|
||||
static int mozplace_delref (struct backend_ctx *, int64_t);
|
||||
static int mozplace_delref (struct backend_ctx *, int64_t, int);
|
||||
static int mozplace_insert (struct backend_ctx *, struct mozplace *);
|
||||
static int mozplace_purge (struct backend_ctx *, int64_t);
|
||||
static int mozplace_update (struct backend_ctx *, struct mozplace *);
|
||||
static int64_t mozplace_url_hash (char const *, size_t);
|
||||
static int parse_mkfsopts (struct bookmarkfs_conf_opt const *,
|
||||
|
@ -397,9 +405,8 @@ bookmark_do_create (
|
|||
stat_buf->value_len = -1;
|
||||
int64_t place_id = 0;
|
||||
if (!is_dir) {
|
||||
char const *url = "about:blank";
|
||||
stat_buf->value_len = strlen(url);
|
||||
status = mozplace_addref(ctx, url, stat_buf->value_len, &place_id,
|
||||
stat_buf->value_len = 0;
|
||||
status = mozplace_addref(ctx, STR_WITHLEN("about:blank"), &place_id,
|
||||
&stat_buf->atime);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
|
@ -457,7 +464,7 @@ bookmark_do_delete (
|
|||
return is_dir ? -ENOTDIR : -EISDIR;
|
||||
}
|
||||
|
||||
status = mozbm_delete(ctx, cols.id, is_dir);
|
||||
status = mozbm_delete(ctx, cols.id, is_dir, true);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
@ -675,7 +682,8 @@ static int
|
|||
mozbm_delete (
|
||||
struct backend_ctx *ctx,
|
||||
int64_t id,
|
||||
bool is_dir
|
||||
bool is_dir,
|
||||
bool purge
|
||||
) {
|
||||
#define MOZBM_DELETE_(cond) \
|
||||
"DELETE FROM `moz_bookmarks` WHERE `id` = ? " cond \
|
||||
|
@ -707,7 +715,7 @@ mozbm_delete (
|
|||
// The ID is alwayed obtained from a previous query in
|
||||
// the same transaction. This shall not happen.
|
||||
xassert(nrows > 0);
|
||||
int status = mozplace_delref(ctx, qctx.place_id);
|
||||
int status = mozplace_delref(ctx, qctx.place_id, purge ? 0 : 1);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
@ -1020,6 +1028,45 @@ mozbm_pos_update (
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
mozbm_purge (
|
||||
struct backend_ctx *ctx,
|
||||
int64_t place_id
|
||||
) {
|
||||
sqlite3_stmt **stmt_ptr = &ctx->stmts[STMT_MOZBM_PURGE];
|
||||
char const *sql = "DELETE FROM `moz_bookmarks` WHERE `fk` = ?";
|
||||
|
||||
int status;
|
||||
DO_QUERY(ctx, stmt_ptr, sql, NULL, NULL, status, , ,
|
||||
DB_QUERY_BIND_INT64(place_id),
|
||||
);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
return sqlite3_changes(ctx->db);
|
||||
}
|
||||
|
||||
static int
|
||||
mozbm_purge_check (
|
||||
struct backend_ctx *ctx,
|
||||
int64_t place_id
|
||||
) {
|
||||
sqlite3_stmt **stmt_ptr = &ctx->stmts[STMT_MOZBM_PURGE_CHECK];
|
||||
char const *sql = "SELECT COUNT(*) FROM `moz_bookmarks` "
|
||||
"WHERE `fk` = ? AND `title` IS NOT NULL";
|
||||
|
||||
int64_t result;
|
||||
ssize_t nrows;
|
||||
DO_QUERY(ctx, stmt_ptr, sql, db_query_i64_cb, &result, nrows, , ,
|
||||
DB_QUERY_BIND_INT64(place_id),
|
||||
);
|
||||
if (nrows < 0) {
|
||||
return nrows;
|
||||
}
|
||||
debug_assert(nrows == 1);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mozbm_update (
|
||||
struct backend_ctx *ctx,
|
||||
|
@ -1086,7 +1133,7 @@ mozkw_delete (
|
|||
char const *name,
|
||||
size_t name_len
|
||||
) {
|
||||
sqlite3_stmt **stmt_ptr = &ctx->stmts[STMT_MOZKW_RENAME];
|
||||
sqlite3_stmt **stmt_ptr = &ctx->stmts[STMT_MOZKW_DELETE];
|
||||
char const *sql = "DELETE FROM `moz_keywords` "
|
||||
"WHERE `keyword` = ? RETURNING `place_id`";
|
||||
|
||||
|
@ -1098,7 +1145,7 @@ mozkw_delete (
|
|||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
status = mozplace_delref(ctx, place_id);
|
||||
status = mozplace_delref(ctx, place_id, 1);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
@ -1153,6 +1200,24 @@ mozkw_lookup (
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mozkw_purge (
|
||||
struct backend_ctx *ctx,
|
||||
int64_t place_id
|
||||
) {
|
||||
sqlite3_stmt **stmt_ptr = &ctx->stmts[STMT_MOZKW_PURGE];
|
||||
char const *sql = "DELETE FROM `moz_keywords` WHERE `place_id` = ?";
|
||||
|
||||
int status;
|
||||
DO_QUERY(ctx, stmt_ptr, sql, NULL, NULL, status, , ,
|
||||
DB_QUERY_BIND_INT64(place_id),
|
||||
);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
return sqlite3_changes(ctx->db);
|
||||
}
|
||||
|
||||
static int
|
||||
mozkw_rename (
|
||||
struct backend_ctx *ctx,
|
||||
|
@ -1173,7 +1238,7 @@ mozkw_rename (
|
|||
if (flags & BOOKMARKFS_BOOKMARK_RENAME_NOREPLACE) {
|
||||
return -EEXIST;
|
||||
}
|
||||
status = mozplace_delref(ctx, old_cols.place_id);
|
||||
status = mozplace_delref(ctx, old_cols.place_id, 1);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
@ -1412,16 +1477,18 @@ mozplace_delete (
|
|||
static int
|
||||
mozplace_delref (
|
||||
struct backend_ctx *ctx,
|
||||
int64_t id
|
||||
int64_t id,
|
||||
int purge
|
||||
) {
|
||||
sqlite3_stmt **stmt_ptr = &ctx->stmts[STMT_MOZPLACE_DELREF];
|
||||
char const *sql =
|
||||
"UPDATE `moz_places` SET `foreign_count` = `foreign_count` - 1 "
|
||||
"UPDATE `moz_places` SET `foreign_count` = `foreign_count` - ? "
|
||||
"WHERE `id` = ? RETURNING `foreign_count`, `origin_id`";
|
||||
|
||||
ssize_t nrows;
|
||||
int64_t result[2]; // `foreign_count`, `origin_id`
|
||||
DO_QUERY(ctx, stmt_ptr, sql, db_query_i64_cb, result, nrows, , ,
|
||||
DB_QUERY_BIND_INT64(purge == 0 ? 1 : purge),
|
||||
DB_QUERY_BIND_INT64(id),
|
||||
);
|
||||
if (nrows < 0) {
|
||||
|
@ -1430,11 +1497,14 @@ mozplace_delref (
|
|||
if (unlikely(nrows == 0)) {
|
||||
return -EIO;
|
||||
}
|
||||
if (result[0] > 0) {
|
||||
if (result[0] == 0) {
|
||||
// `foreign_count` reaches 0, delete row.
|
||||
return mozplace_delete(ctx, id, result[1]);
|
||||
}
|
||||
if (purge > 0) {
|
||||
return 0;
|
||||
}
|
||||
// `foreign_count` reaches 0, delete row.
|
||||
return mozplace_delete(ctx, id, result[1]);
|
||||
return mozplace_purge(ctx, id);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1469,6 +1539,33 @@ mozplace_insert (
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mozplace_purge (
|
||||
struct backend_ctx *ctx,
|
||||
int64_t id
|
||||
) {
|
||||
int status = mozbm_purge_check(ctx, id);
|
||||
if (status <= 0) {
|
||||
return status;
|
||||
}
|
||||
status = mozbm_purge(ctx, id);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
int changes = status;
|
||||
|
||||
status = mozkw_purge(ctx, id);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
changes += status;
|
||||
|
||||
if (changes == 0) {
|
||||
return 0;
|
||||
}
|
||||
return mozplace_delref(ctx, id, changes);
|
||||
}
|
||||
|
||||
static int
|
||||
mozplace_update (
|
||||
struct backend_ctx *ctx,
|
||||
|
@ -1482,7 +1579,7 @@ mozplace_update (
|
|||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
status = mozplace_delref(ctx, cols->id);
|
||||
status = mozplace_delref(ctx, cols->id, 1);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
@ -2000,7 +2097,7 @@ tag_entry_delete (
|
|||
return status;
|
||||
}
|
||||
|
||||
status = mozbm_delete(ctx, cols.id, false);
|
||||
status = mozbm_delete(ctx, cols.id, false, false);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
@ -2092,7 +2189,10 @@ bookmark_do_get (
|
|||
#define BOOKMARK_GET_(cols, join) "SELECT CASE ? " cols "END " \
|
||||
"FROM `moz_bookmarks` `b` " join "WHERE `b`.`id` = ?"
|
||||
#define BOOKMARK_GET(cols) BOOKMARK_GET_(cols \
|
||||
"WHEN " STRINGIFY(BM_XATTR_DATE_ADDED) " THEN `b`.`dateAdded` ", )
|
||||
"WHEN " STRINGIFY(BM_XATTR_DATE_ADDED) " THEN `b`.`dateAdded` " \
|
||||
"WHEN " STRINGIFY(BM_XATTR_KEYWORD) " THEN " \
|
||||
"(SELECT `keyword` FROM `moz_keywords` `k` " \
|
||||
"WHERE `k`.`place_id` = `b`.`fk`) ", )
|
||||
#define BOOKMARK_GET_EX BOOKMARK_GET_( \
|
||||
"WHEN " STRINGIFY(BM_XATTR_NULL) " THEN `p`.`url` " \
|
||||
"WHEN " STRINGIFY(BM_XATTR_DESC) " THEN `p`.`description` " \
|
||||
|
@ -2637,6 +2737,9 @@ get_xattr_id (
|
|||
return BM_XATTR_GUID;
|
||||
}
|
||||
}
|
||||
if (0 == strcmp("keyword", name)) {
|
||||
return BM_XATTR_KEYWORD;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -2942,9 +3045,9 @@ backend_create (
|
|||
resp_flags |= BOOKMARKFS_BACKEND_EXCLUSIVE;
|
||||
}
|
||||
|
||||
char const *xattr_names = "guid\0date_added\0description\0";
|
||||
char const *xattr_names = "guid\0date_added\0description\0keyword\0";
|
||||
if (opts.flags & BACKEND_FILENAME_GUID) {
|
||||
xattr_names = "title\0date_added\0description\0";
|
||||
xattr_names = "title\0date_added\0description\0keyword\0";
|
||||
}
|
||||
|
||||
resp->name = "firefox";
|
||||
|
@ -3611,7 +3714,7 @@ bookmark_rename (
|
|||
status = (old_cols.place_id == 0) ? -ENOTDIR : -EISDIR;
|
||||
goto fail;
|
||||
}
|
||||
status = mozbm_delete(ctx, new_cols.id, old_cols.place_id == 0);
|
||||
status = mozbm_delete(ctx, new_cols.id, old_cols.place_id == 0, true);
|
||||
if (status < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -3724,6 +3827,9 @@ bookmark_set (
|
|||
}
|
||||
break;
|
||||
|
||||
case BM_XATTR_KEYWORD:
|
||||
return -EPERM;
|
||||
|
||||
default:
|
||||
return -ENOATTR;
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ struct xattr_get_ctx {
|
|||
char eol;
|
||||
|
||||
unsigned binary : 1;
|
||||
unsigned multi_name : 1;
|
||||
unsigned multi_attr : 1;
|
||||
};
|
||||
|
||||
// Forward declaration start
|
||||
|
@ -249,8 +249,8 @@ subcmd_xattr_get (
|
|||
ctx.binary = 1;
|
||||
break;
|
||||
}
|
||||
OPT_OPT('m') {
|
||||
ctx.multi_name = 1;
|
||||
OPT_OPT('a') {
|
||||
ctx.multi_attr = 1;
|
||||
break;
|
||||
}
|
||||
OPT_OPT('q') {
|
||||
|
@ -269,7 +269,7 @@ subcmd_xattr_get (
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (ctx.multi_name) {
|
||||
if (ctx.multi_attr) {
|
||||
return xattr_get_one(argv[argc - 1], argv, argc - 1, &ctx);
|
||||
}
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
|
@ -416,7 +416,7 @@ xattr_get_one (
|
|||
for (int i = 0; i < names_cnt; ++i) {
|
||||
char const *name = names[i];
|
||||
|
||||
ctx->prefix = ctx->multi_name ? name : path;
|
||||
ctx->prefix = ctx->multi_attr ? name : path;
|
||||
status = bookmarkfs_xattr_get(fd, name, xattr_get_cb, ctx);
|
||||
if (status < 0) {
|
||||
goto end;
|
||||
|
|
28
src/fs_ops.c
28
src/fs_ops.c
|
@ -409,6 +409,12 @@ bm_create (
|
|||
}
|
||||
}
|
||||
|
||||
if (flags & O_TRUNC) {
|
||||
// See bm_open() for comments on O_RDONLY|O_TRUNC.
|
||||
if ((flags & O_ACCMODE) != O_RDONLY) {
|
||||
stat.value_len = 0;
|
||||
}
|
||||
}
|
||||
bm_fillstat(&stat, SUBSYS_TYPE_BOOKMARK, false, stat_buf);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1346,12 +1352,14 @@ intfs_delete (
|
|||
) {
|
||||
int status = -EPERM;
|
||||
|
||||
int64_t bm_parent_id = BOOKMARKS_ROOT_ID;
|
||||
switch (parent_id) {
|
||||
case INTFS_ID_TAGS:
|
||||
flags |= BOOKMARKFS_BOOKMARK_TYPE(TAG);
|
||||
bm_parent_id = TAGS_ROOT_ID;
|
||||
// fallthrough
|
||||
case INTFS_ID_BOOKMARKS:
|
||||
status = bm_delete(BOOKMARKS_ROOT_ID, name, flags);
|
||||
status = bm_delete(bm_parent_id, name, flags);
|
||||
break;
|
||||
|
||||
case INTFS_ID_KEYWORDS:
|
||||
|
@ -1524,25 +1532,25 @@ intfs_opendir (
|
|||
fuse_ino_t ino,
|
||||
struct fuse_file_info *fi
|
||||
) {
|
||||
int status = 0;
|
||||
|
||||
uint64_t bm_id = BOOKMARKS_ROOT_ID;
|
||||
uint32_t flags = 0;
|
||||
switch (id) {
|
||||
case INTFS_ID_ROOT:
|
||||
fi->cache_readdir = 1;
|
||||
fi->keep_cache = 1;
|
||||
break;
|
||||
|
||||
case INTFS_ID_BOOKMARKS:
|
||||
status = bm_opendir(BOOKMARKS_ROOT_ID, ino, flags, fi);
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case INTFS_ID_TAGS:
|
||||
flags |= BOOKMARKFS_BOOKMARK_TYPE(TAG);
|
||||
status = bm_opendir(TAGS_ROOT_ID, ino, flags, fi);
|
||||
bm_id = TAGS_ROOT_ID;
|
||||
break;
|
||||
|
||||
case INTFS_ID_KEYWORDS:
|
||||
flags |= BOOKMARKFS_BOOKMARK_TYPE(KEYWORD);
|
||||
bm_id = 0;
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
return bm_opendir(bm_id, ino, flags, fi);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -28,10 +28,8 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "backend.h"
|
||||
|
@ -67,10 +65,6 @@ struct fsck_dir {
|
|||
#define FSCK_DIR_DONE ( 1u << 0 )
|
||||
|
||||
// Forward declaration start
|
||||
#ifdef __linux__
|
||||
static ssize_t getdents_ (int, void *, size_t);
|
||||
#endif
|
||||
|
||||
static int next_subdir (struct fsck_ctx *, struct fsck_dir *,
|
||||
struct dirent const **);
|
||||
static int open_subdir (int, char const *, uint64_t *);
|
||||
|
@ -79,22 +73,6 @@ static void print_help (void);
|
|||
static void print_version (void);
|
||||
// Forward declaration end
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
// Some libc (e.g., musl) may declare a getdents() function
|
||||
// in dirent.h with conflicting types.
|
||||
#define getdents getdents_
|
||||
static ssize_t
|
||||
getdents_ (
|
||||
int dirfd,
|
||||
void *buf,
|
||||
size_t bufsize
|
||||
) {
|
||||
return syscall(SYS_getdents64, dirfd, buf, bufsize);
|
||||
}
|
||||
|
||||
#endif /* defined(__linux__) */
|
||||
|
||||
static int
|
||||
next_subdir (
|
||||
struct fsck_ctx *ctx,
|
||||
|
@ -112,7 +90,7 @@ next_subdir (
|
|||
ctx->dent_buf = xrealloc(ctx->dent_buf, ctx->dent_buf_size);
|
||||
}
|
||||
ssize_t nbytes
|
||||
= getdents(dir->fd, ctx->dent_buf + start, DIRENT_BUFSIZE);
|
||||
= xgetdents(dir->fd, ctx->dent_buf + start, DIRENT_BUFSIZE);
|
||||
if (nbytes < 0) {
|
||||
log_printf("getdents(): %s", xstrerror(errno));
|
||||
return -1;
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
#define BOOKMARKFS_VER_MAJOR 0
|
||||
#define BOOKMARKFS_VER_MINOR 1
|
||||
#define BOOKMARKFS_VER_PATCH 0
|
||||
#define BOOKMARKFS_VER_PATCH 1
|
||||
|
||||
#define bookmarkfs_make_vernum(major, minor, patch) \
|
||||
( ((major) << 16) | ((minor) << 8) | ((patch) << 0) )
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
#ifdef HAVE___BUILTIN_EXPECT
|
||||
|
@ -84,6 +87,12 @@
|
|||
} while (0)
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
# define xgetdents(fd, buf, bufsz) syscall(SYS_getdents64, fd, buf, bufsz)
|
||||
#elif defined(__FreeBSD__)
|
||||
# define xgetdents(fd, buf, bufsz) getdents(fd, buf, bufsz)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Prints a message to standard error, and then aborts.
|
||||
*/
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
EXTRA_DIST = package.m4 testsuite.at $(TESTSUITE) $(TESTS_)
|
||||
|
||||
TESTS_ = lib_hash.at lib_prng.at lib_watcher.at lib_sandbox.at \
|
||||
lib_hashmap.at fs_basic.at fs_regrw.at
|
||||
lib_hashmap.at fs_basic.at fs_regrw.at fs_dents.at fs_assoc.at
|
||||
|
||||
# Helper programs for testing
|
||||
|
||||
|
@ -35,7 +35,7 @@ if BOOKMARKFS_MOUNT
|
|||
if BOOKMARKFS_UTIL
|
||||
check_fs_CPPFLAGS += -DHAVE_BOOKMARKFS_UTIL
|
||||
check_fs_LDADD += $(top_builddir)/src/libbookmarkfs_util.la
|
||||
check_fs_SOURCES += check_fs_regrw.c check_util.c
|
||||
check_fs_SOURCES += check_fs_regrw.c check_fs_dents.c check_util.c
|
||||
endif # BOOKMARKFS_UTIL
|
||||
endif # BOOKMARKFS_MOUNT
|
||||
|
||||
|
|
|
@ -57,6 +57,8 @@ dispatch_subcmds (
|
|||
#ifdef HAVE_BOOKMARKFS_UTIL
|
||||
} else if (0 == strcmp("regrw", cmd)) {
|
||||
status = check_fs_regrw(argc, argv);
|
||||
} else if (0 == strcmp("dents", cmd)) {
|
||||
status = check_fs_dents(argc, argv);
|
||||
#endif
|
||||
} else {
|
||||
log_printf("bad subcmd '%s'", cmd);
|
||||
|
|
282
tests/check_fs_dents.c
Normal file
282
tests/check_fs_dents.c
Normal file
|
@ -0,0 +1,282 @@
|
|||
/**
|
||||
* bookmarkfs/tests/check_fs_dents.c
|
||||
* ----
|
||||
*
|
||||
* Copyright (C) 2025 CismonX <admin@cismon.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "check_util.h"
|
||||
#include "frontend_util.h"
|
||||
#include "ioctl.h"
|
||||
#include "prng.h"
|
||||
|
||||
#define ITEM_DELETED ( 1u << 0 )
|
||||
#define ITEM_DIRTY ( 1u << 1 )
|
||||
#define ITEM_MARKED ( 1u << 2 )
|
||||
|
||||
struct check_item {
|
||||
int id;
|
||||
unsigned flags;
|
||||
};
|
||||
|
||||
// Forward declaration start
|
||||
static int dent_check (int, struct check_item *, int const *, int, int);
|
||||
static int dent_delete (int, struct check_item *);
|
||||
static int dent_new (int, struct check_item *);
|
||||
static int dent_permute (int, struct check_item *, struct check_item *);
|
||||
static int do_check_fs_dents (int, int);
|
||||
// Forward declaration end
|
||||
|
||||
static int
|
||||
dent_check (
|
||||
int dirfd,
|
||||
struct check_item *item,
|
||||
int const *map,
|
||||
int n,
|
||||
int ignore_dirty
|
||||
) {
|
||||
char buf[4096];
|
||||
struct check_item const *last_found = NULL;
|
||||
for (ssize_t off = 0, len = 0; ; ) {
|
||||
if (off == len) {
|
||||
len = xgetdents(dirfd, buf, sizeof(buf));
|
||||
if (len < 0) {
|
||||
log_printf("getdents(): %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (len == 0) {
|
||||
break;
|
||||
}
|
||||
off = 0;
|
||||
}
|
||||
struct dirent *dent = (struct dirent *)(buf + off);
|
||||
off += dent->d_reclen;
|
||||
|
||||
int id = atoi(dent->d_name);
|
||||
if (id < 0 || id >= n) {
|
||||
return -1;
|
||||
}
|
||||
struct check_item *found = item + map[id];
|
||||
if (found->flags & (ITEM_DELETED | ITEM_MARKED)) {
|
||||
return -1;
|
||||
}
|
||||
if (ignore_dirty && found->flags & ITEM_DIRTY) {
|
||||
continue;
|
||||
}
|
||||
if (last_found != NULL && found <= last_found) {
|
||||
return -1;
|
||||
}
|
||||
last_found = found;
|
||||
found->flags |= ITEM_MARKED;
|
||||
}
|
||||
|
||||
for (last_found = item + n; item < last_found; ++item) {
|
||||
if (ignore_dirty && item->flags & ITEM_DIRTY) {
|
||||
continue;
|
||||
}
|
||||
if (!(item->flags & (ITEM_DELETED | ITEM_MARKED))) {
|
||||
return -1;
|
||||
}
|
||||
item->flags &= ~ITEM_MARKED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dent_delete (
|
||||
int dirfd,
|
||||
struct check_item *item
|
||||
) {
|
||||
if (item->flags & ITEM_DELETED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char name[16];
|
||||
sprintf(name, "%d", item->id);
|
||||
if (0 != unlinkat(dirfd, name, 0)) {
|
||||
log_printf("unlinkat(): %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
item->flags |= ITEM_DELETED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dent_new (
|
||||
int dirfd,
|
||||
struct check_item *item
|
||||
) {
|
||||
char name[16];
|
||||
sprintf(name, "%d", item->id);
|
||||
int fd = openat(dirfd, name, O_CREAT | O_EXCL, 0600);
|
||||
if (fd < 0) {
|
||||
log_printf("openat(): %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
item->flags &= ~ITEM_DELETED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dent_permute (
|
||||
int dirfd,
|
||||
struct check_item *item1,
|
||||
struct check_item *item2
|
||||
) {
|
||||
int op = BOOKMARKFS_PERMD_OP_SWAP;
|
||||
struct check_item *itemx = item2;
|
||||
if ((item1->flags & ITEM_DELETED) || (item2->flags & ITEM_DELETED)) {
|
||||
if (item1->flags & ITEM_DELETED) {
|
||||
struct check_item *item_tmp = item1;
|
||||
item1 = item2;
|
||||
item2 = item_tmp;
|
||||
}
|
||||
if (item1 > item2) {
|
||||
op = BOOKMARKFS_PERMD_OP_MOVE_BEFORE;
|
||||
for (itemx = item2 + 1; itemx->flags & ITEM_DELETED; ++itemx);
|
||||
} else {
|
||||
op = BOOKMARKFS_PERMD_OP_MOVE_AFTER;
|
||||
for (itemx = item2 - 1; itemx->flags & ITEM_DELETED; --itemx);
|
||||
}
|
||||
}
|
||||
|
||||
struct bookmarkfs_permd_data permd_data;
|
||||
permd_data.op = op;
|
||||
sprintf(permd_data.name1, "%d", item1->id);
|
||||
sprintf(permd_data.name2, "%d", itemx->id);
|
||||
if (0 != ioctl(dirfd, BOOKMARKFS_IOC_PERMD, &permd_data)) {
|
||||
log_printf("ioctl(): %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct check_item item_tmp = *item1;
|
||||
*item1 = *item2;
|
||||
*item2 = item_tmp;
|
||||
item1->flags |= ITEM_DIRTY;
|
||||
item2->flags |= ITEM_DIRTY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
do_check_fs_dents (
|
||||
int dirfd,
|
||||
int n
|
||||
) {
|
||||
#define ASSERT_EQ(val, expr) ASSERT_EXPR_INT(expr, r_, (val) == r_, goto end;)
|
||||
#define ASSERT_NE(val, expr) ASSERT_EXPR_INT(expr, r_, (val) != r_, goto end;)
|
||||
|
||||
struct check_item *items = calloc(n, sizeof(struct check_item));
|
||||
int *map = malloc(sizeof(int) * n);
|
||||
if (items == NULL || map == NULL) {
|
||||
return -1;
|
||||
}
|
||||
int status = -1;
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
struct check_item *item = items + i;
|
||||
map[i] = item->id = i;
|
||||
ASSERT_EQ(0, dent_new(dirfd, item));
|
||||
}
|
||||
|
||||
ASSERT_EQ(0, lseek(dirfd, 0, SEEK_SET));
|
||||
for (int i = 0; i < n / 4; ++i) {
|
||||
uint64_t bits = prng_rand();
|
||||
struct check_item *i1 = &items[bits % n];
|
||||
struct check_item *i2 = &items[(bits >> 32) % n];
|
||||
|
||||
if (i1 == i2 || bits >> 63) {
|
||||
ASSERT_EQ(0, dent_delete(dirfd, i1));
|
||||
ASSERT_EQ(0, dent_delete(dirfd, i2));
|
||||
#if !defined(__FreeBSD__)
|
||||
} else {
|
||||
if (i1->flags & ITEM_DELETED && i2->flags & ITEM_DELETED) {
|
||||
continue;
|
||||
}
|
||||
ASSERT_EQ(0, dent_permute(dirfd, i1, i2));
|
||||
map[i1->id] = i1 - items;
|
||||
map[i2->id] = i2 - items;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(0, dent_check(dirfd, items, map, n, 1));
|
||||
ASSERT_EQ(0, lseek(dirfd, 0, SEEK_SET));
|
||||
ASSERT_EQ(0, dent_check(dirfd, items, map, n, 0));
|
||||
status = 0;
|
||||
|
||||
end:
|
||||
free(items);
|
||||
free(map);
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
check_fs_dents (
|
||||
int argc,
|
||||
char *argv[]
|
||||
) {
|
||||
uint64_t seed_buf[4], *seed = NULL;
|
||||
int n = -1;
|
||||
|
||||
OPT_START(argc, argv, "n:s:")
|
||||
OPT_OPT('n') {
|
||||
n = atoi(optarg);
|
||||
break;
|
||||
}
|
||||
OPT_OPT('s') {
|
||||
if (0 != prng_seed_from_hex(seed_buf, optarg)) {
|
||||
return -1;
|
||||
}
|
||||
seed = seed_buf;
|
||||
break;
|
||||
}
|
||||
OPT_END
|
||||
|
||||
if (n <= 0) {
|
||||
log_printf("bad size %d", n);
|
||||
return -1;
|
||||
}
|
||||
if (argc < 1) {
|
||||
log_puts("path not given");
|
||||
return -1;
|
||||
}
|
||||
char const *path = argv[0];
|
||||
|
||||
if (0 != prng_seed(seed)) {
|
||||
return -1;
|
||||
}
|
||||
int dirfd = open(path, O_RDONLY | O_DIRECTORY);
|
||||
if (dirfd < 0) {
|
||||
log_printf("open: %s: %s", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
int status = do_check_fs_dents(dirfd, n);
|
||||
close(dirfd);
|
||||
return status;
|
||||
}
|
|
@ -122,7 +122,7 @@ do_check_fs_regrw (
|
|||
#ifndef O_DIRECT
|
||||
# define O_DIRECT 0
|
||||
#endif
|
||||
int fd = open(path, O_RDWR | O_TRUNC | O_DIRECT);
|
||||
int fd = open(path, O_RDWR | O_CREAT | O_TRUNC | O_DIRECT);
|
||||
ASSERT_NE(-1, fd);
|
||||
|
||||
struct stat stat_buf;
|
||||
|
|
|
@ -35,6 +35,12 @@
|
|||
action_if_false \
|
||||
} while (0)
|
||||
|
||||
int
|
||||
check_fs_dents (
|
||||
int argc,
|
||||
char *argv[]
|
||||
);
|
||||
|
||||
int
|
||||
check_fs_regrw (
|
||||
int argc,
|
||||
|
|
65
tests/fs_assoc.at
Normal file
65
tests/fs_assoc.at
Normal file
|
@ -0,0 +1,65 @@
|
|||
dnl
|
||||
dnl Copyright (C) 2025 CismonX <admin@cismon.net>
|
||||
dnl
|
||||
dnl Copying and distribution of this file, with or without modification,
|
||||
dnl are permitted in any medium without royalty,
|
||||
dnl provided the copyright notice and this notice are preserved.
|
||||
dnl This file is offered as-is, without any warranty.
|
||||
dnl
|
||||
|
||||
AT_SETUP([fs: tags and keywords])
|
||||
AT_KEYWORDS([fs assoc tag keyword])
|
||||
|
||||
ATX_CHECK_FS_NEW_ASSOC([eol], , [
|
||||
ATX_RUN_REPEAT([8], [
|
||||
name=$(ath_fn_rand_u64_hex)
|
||||
tag=$(ath_fn_rand_u64_hex)
|
||||
keyword=$(ath_fn_rand_u64_hex)
|
||||
content=foo:$(ath_fn_rand_u64_hex)
|
||||
|
||||
ATX_RUN([
|
||||
echo "$content/1" > $name-1
|
||||
echo "$content/2" > $name-2
|
||||
echo "$content/3" > $name-3
|
||||
mkdir "$atx_tags/$tag-1" "$atx_tags/$tag-2" "$atx_tags/$tag-3"
|
||||
|
||||
ln $name-1 $name-2 "$atx_tags/$tag-1"
|
||||
ln $name-2 $name-3 "$atx_tags/$tag-2"
|
||||
ln $name-3 $name-1 "$atx_tags/$tag-3"
|
||||
test $name-1 -ef "$atx_tags/$tag-1/$name-1"
|
||||
test $name-1 -ef "$atx_tags/$tag-3/$name-1"
|
||||
test $name-2 -ef "$atx_tags/$tag-1/$name-2"
|
||||
test $name-2 -ef "$atx_tags/$tag-2/$name-2"
|
||||
test $name-3 -ef "$atx_tags/$tag-3/$name-3"
|
||||
test $name-3 -ef "$atx_tags/$tag-2/$name-3"
|
||||
|
||||
ln $name-1 "$atx_keywords/$keyword-2"
|
||||
ln $name-2 "$atx_keywords/$keyword-3"
|
||||
ln $name-3 "$atx_keywords/$keyword-1"
|
||||
test $name-1 -ef "$atx_keywords/$keyword-2"
|
||||
test $name-2 -ef "$atx_keywords/$keyword-3"
|
||||
test $name-3 -ef "$atx_keywords/$keyword-1"
|
||||
|
||||
rm "$atx_tags/$tag-1/$name-1"
|
||||
rm "$atx_tags/$tag-2/$name-2"
|
||||
rm "$atx_tags/$tag-3/$name-3"
|
||||
test ! -e "$atx_tags/$tag-1/$name-1"
|
||||
test ! -e "$atx_tags/$tag-2/$name-2"
|
||||
test ! -e "$atx_tags/$tag-3/$name-3"
|
||||
|
||||
rm "$atx_keywords/$keyword-1"
|
||||
rm "$atx_keywords/$keyword-2"
|
||||
test ! -e "$atx_keywords/$keyword-1"
|
||||
test ! -e "$atx_keywords/$keyword-2"
|
||||
|
||||
rm $name-1 $name-2 $name-3
|
||||
test ! -e "$atx_tags/$tag-1/$name-2"
|
||||
test ! -e "$atx_tags/$tag-2/$name-3"
|
||||
test ! -e "$atx_tags/$tag-3/$name-1"
|
||||
test ! -e "$atx_keywords/$keyword-3"
|
||||
rmdir "$atx_tags/$tag-1" "$atx_tags/$tag-2" "$atx_tags/$tag-3"
|
||||
])
|
||||
])
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
|
@ -15,53 +15,48 @@ AT_KEYWORDS([fs basic])
|
|||
ATX_CHECK_FS_NEW_ANY([eol], , [
|
||||
ATX_RUN_REPEAT([8], [
|
||||
name=$(ath_fn_rand_u64_hex)
|
||||
name_1=${name}_1
|
||||
name_2=${name}_2
|
||||
|
||||
content=foo:$(ath_fn_rand_u64_hex)
|
||||
content_1=${content}/1
|
||||
content_2=${content}/2
|
||||
|
||||
ATX_RUN([
|
||||
echo "$content_1" > $name_1
|
||||
test "$(cat $name_1)" = "$content_1"
|
||||
echo "$content_2" > $name_2
|
||||
test "$(cat $name_2)" = "$content_2"
|
||||
echo "$content/1" > $name-1
|
||||
test "$(cat $name-1)" = "$content/1"
|
||||
echo "$content/2" > $name-2
|
||||
test "$(cat $name-2)" = "$content/2"
|
||||
|
||||
mv $name_1 $name_2
|
||||
test ! -e $name_1
|
||||
test "$(cat $name_2)" = "$content_1"
|
||||
mv $name-1 $name-2
|
||||
test ! -e $name-1
|
||||
test "$(cat $name-2)" = "$content/1"
|
||||
|
||||
mv $name_2 $name_1
|
||||
test ! -e $name_2
|
||||
test "$(cat $name_1)" = "$content_1"
|
||||
mv $name-2 $name-1
|
||||
test ! -e $name-2
|
||||
test "$(cat $name-1)" = "$content/1"
|
||||
|
||||
mkdir $name_2
|
||||
mv $name_1 $name_2/$name_2
|
||||
test ! -e $name_1
|
||||
test "$(cat $name_2/$name_2)" = "$content_1"
|
||||
mkdir $name-2
|
||||
mv $name-1 $name-2/$name-2
|
||||
test ! -e $name-1
|
||||
test "$(cat $name-2/$name-2)" = "$content/1"
|
||||
|
||||
! mkdir $name_2/$name_2
|
||||
mkdir $name_2/$name_1
|
||||
mv $name_2/$name_2 $name_2/$name_1/$name_1
|
||||
test "$(cat $name_2/$name_1/$name_1)" = "$content_1"
|
||||
! mkdir $name-2/$name-2
|
||||
mkdir $name-2/$name-1
|
||||
mv $name-2/$name-2 $name-2/$name-1/$name-1
|
||||
test "$(cat $name-2/$name-1/$name-1)" = "$content/1"
|
||||
|
||||
mkdir $name_1
|
||||
! mv $name_1 $name_2/$name_1/$name_1
|
||||
! mv $name_1 $name_2
|
||||
mkdir $name-1
|
||||
! mv $name-1 $name-2/$name-1/$name-1
|
||||
! mv $name-1 $name-2
|
||||
|
||||
! mv $name_2/$name_1/$name_1 $name_2
|
||||
rm $name_2/$name_1/$name_1
|
||||
test ! -e $name_2/$name_1/$name_1
|
||||
! mv $name-2/$name-1/$name-1 $name-2
|
||||
rm $name-2/$name-1/$name-1
|
||||
test ! -e $name-2/$name-1/$name-1
|
||||
|
||||
mv $name_1 $name_2
|
||||
test ! -e $name_1
|
||||
test -d $name_2/$name_1
|
||||
mv $name-1 $name-2
|
||||
test ! -e $name-1
|
||||
test -d $name-2/$name-1
|
||||
|
||||
! rmdir $name_2
|
||||
rmdir $name_2/$name_1
|
||||
rmdir $name_2
|
||||
test ! -e $name_2
|
||||
! rmdir $name-2
|
||||
rmdir $name-2/$name-1
|
||||
rmdir $name-2
|
||||
test ! -e $name-2
|
||||
])
|
||||
])
|
||||
])
|
||||
|
|
29
tests/fs_dents.at
Normal file
29
tests/fs_dents.at
Normal file
|
@ -0,0 +1,29 @@
|
|||
dnl
|
||||
dnl Copyright (C) 2025 CismonX <admin@cismon.net>
|
||||
dnl
|
||||
dnl Copying and distribution of this file, with or without modification,
|
||||
dnl are permitted in any medium without royalty,
|
||||
dnl provided the copyright notice and this notice are preserved.
|
||||
dnl This file is offered as-is, without any warranty.
|
||||
dnl
|
||||
|
||||
AT_SETUP([fs: directory entries])
|
||||
AT_KEYWORDS([fs dents])
|
||||
|
||||
ATX_CHECK_FS_NEW_ANY(, [
|
||||
# requires PRNG
|
||||
ATX_FEAT_PREREQ([bookmarkfs-util])
|
||||
], [
|
||||
ATX_RUN_REPEAT([8], [
|
||||
name=$(ath_fn_rand_u64_hex)
|
||||
seed=$(ath_fn_prng_seed)
|
||||
echo "prng seed: $seed"
|
||||
|
||||
mkdir $name
|
||||
ATX_RUN([
|
||||
check-fs dents -n 1024 -s "$seed" $name
|
||||
])
|
||||
])
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
|
@ -19,7 +19,6 @@ ATX_CHECK_FS_NEW_ANY([file_max=524288], [
|
|||
echo "prng seed: $seed"
|
||||
|
||||
ATX_RUN([
|
||||
touch $name
|
||||
check-fs regrw -n 524288 -s "$seed" $name
|
||||
])
|
||||
])
|
||||
|
|
|
@ -139,6 +139,7 @@ m4_define([ATX_CHECK_FS], [
|
|||
fi
|
||||
done
|
||||
$6
|
||||
ATX_RUN_ONE([check-fs ismount "$4"])
|
||||
], [
|
||||
umount "$4"
|
||||
])
|
||||
|
@ -173,6 +174,17 @@ m4_define([ATX_CHECK_FS_NEW_ANY], [
|
|||
])
|
||||
])
|
||||
|
||||
dnl
|
||||
dnl ATX_CHECK_FS_NEW_ASSOC([options], [prepare], [check])
|
||||
dnl
|
||||
m4_define([ATX_CHECK_FS_NEW_ASSOC], [
|
||||
ATX_CHECK_FS_NEW([firefox], [$1], [mnt.tmp], [$2], [
|
||||
atx_tags=../../tags
|
||||
atx_keywords=../../keywords
|
||||
ATX_RUN_IN_DIR([mnt.tmp/bookmarks/unfiled], [$3])
|
||||
])
|
||||
])
|
||||
|
||||
dnl -- Helper functions --
|
||||
|
||||
AT_TEST_HELPER_FN([rand_u64_hex], , , [
|
||||
|
@ -206,3 +218,5 @@ m4_include([lib_hashmap.at])
|
|||
AT_BANNER([The Filesystem])
|
||||
m4_include([fs_basic.at])
|
||||
m4_include([fs_regrw.at])
|
||||
m4_include([fs_dents.at])
|
||||
m4_include([fs_assoc.at])
|
||||
|
|
Loading…
Add table
Reference in a new issue