diff --git a/.github/workflows/rpm-build-and-test.yml b/.github/workflows/rpm-build-and-test.yml index bbad2b715b4..232b3cbd971 100644 --- a/.github/workflows/rpm-build-and-test.yml +++ b/.github/workflows/rpm-build-and-test.yml @@ -5,10 +5,10 @@ env: # build is done on the lowest version and test on the highest with a "sanity test" # stage done on all versions in the list ecept the highest EL8_BUILD_VERSION: 8.6 - EL8_VERSION: 8 + EL8_VERSION: 8.8 EL9_BUILD_VERSION: 9 EL9_VERSION: 9 - LEAP15_VERSION: 15.4 + LEAP15_VERSION: 15.5 on: workflow_dispatch: diff --git a/ci/functional/test_main.sh b/ci/functional/test_main.sh index d318b3601e3..56fe36f8571 100755 --- a/ci/functional/test_main.sh +++ b/ci/functional/test_main.sh @@ -45,7 +45,7 @@ test_cluster() { FIRST_NODE=${first_node} \ TEST_RPMS=${TEST_RPMS} \ NODELIST=${tnodes} \ - BUILD_URL=\"$BUILD_URL\" \ + BUILD_URL=\"${BUILD_URL:-Unknown in GHA}\" \ STAGE_NAME=\"$STAGE_NAME\" \ $(cat ci/functional/test_main_prep_node.sh)" } diff --git a/src/cart/crt_iv.c b/src/cart/crt_iv.c index af3226facd8..59ad504993f 100644 --- a/src/cart/crt_iv.c +++ b/src/cart/crt_iv.c @@ -3508,8 +3508,8 @@ crt_iv_update_internal(crt_iv_namespace_t ivns, uint32_t class_id, D_GOTO(exit, rc); } else { - DL_CDEBUG(rc == -DER_NONEXIST || rc == -DER_NOTLEADER, DLOG_INFO, DLOG_ERR, rc, - "ivo_on_update failed"); + DL_CDEBUG(rc == -DER_NONEXIST || rc == -DER_NOTLEADER || rc == -DER_BUSY, + DLOG_INFO, DLOG_ERR, rc, "ivo_on_update failed"); update_comp_cb(ivns, class_id, iv_key, NULL, iv_value, rc, cb_arg); diff --git a/src/client/dfs/dfs.c b/src/client/dfs/dfs.c index a95acd9d31b..e05ed2e50bc 100644 --- a/src/client/dfs/dfs.c +++ b/src/client/dfs/dfs.c @@ -1,5 +1,5 @@ /** - * (C) Copyright 2018-2023 Intel Corporation. + * (C) Copyright 2018-2024 Intel Corporation. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -3416,13 +3416,10 @@ lookup_rel_path(dfs_t *dfs, dfs_obj_t *root, const char *path, int flags, lookup_rel_path_loop: /* - * Open the directory object one level up. - * Since fetch_entry does not support ".", - * we can't support ".." as the last entry, - * nor can we support "../.." because we don't - * have parent.parent_oid and parent.mode. - * For now, represent this partial state with - * parent_fully_valid. + * Open the directory object one level up. Since fetch_entry does not support ".", + * we can't support ".." as the last entry, nor can we support "../.." because we + * don't have parent.parent_oid and parent.mode. For now, represent this partial + * state with parent_fully_valid. */ parent_fully_valid = true; if (strcmp(token, "..") == 0) { @@ -3508,15 +3505,23 @@ lookup_rel_path(dfs_t *dfs, dfs_obj_t *root, const char *path, int flags, } if (stbuf) { - daos_size_t size; + daos_array_stbuf_t array_stbuf = {0}; - rc = daos_array_get_size(obj->oh, DAOS_TX_NONE, &size, NULL); + rc = daos_array_stat(obj->oh, DAOS_TX_NONE, &array_stbuf, NULL); if (rc) { daos_array_close(obj->oh, NULL); D_GOTO(err_obj, rc = daos_der2errno(rc)); } - stbuf->st_size = size; + + stbuf->st_size = array_stbuf.st_size; stbuf->st_blocks = (stbuf->st_size + (1 << 9) - 1) >> 9; + + rc = update_stbuf_times(entry, array_stbuf.st_max_epoch, stbuf, + NULL); + if (rc) { + daos_array_close(obj->oh, NULL); + D_GOTO(err_obj, rc); + } } break; } @@ -3617,14 +3622,28 @@ lookup_rel_path(dfs_t *dfs, dfs_obj_t *root, const char *path, int flags, } obj->d.chunk_size = entry.chunk_size; - obj->d.oclass = entry.oclass; - if (stbuf) - stbuf->st_size = sizeof(entry); - + obj->d.oclass = entry.oclass; oid_cp(&parent.oid, obj->oid); oid_cp(&parent.parent_oid, obj->parent_oid); parent.oh = obj->oh; parent.mode = entry.mode; + + if (stbuf) { + daos_epoch_t ep; + + rc = daos_obj_query_max_epoch(obj->oh, DAOS_TX_NONE, &ep, NULL); + if (rc) { + daos_obj_close(obj->oh, NULL); + D_GOTO(err_obj, rc = daos_der2errno(rc)); + } + + rc = update_stbuf_times(entry, ep, stbuf, NULL); + if (rc) { + daos_obj_close(obj->oh, NULL); + D_GOTO(err_obj, rc = daos_der2errno(rc)); + } + stbuf->st_size = sizeof(entry); + } } if (mode) @@ -3632,16 +3651,49 @@ lookup_rel_path(dfs_t *dfs, dfs_obj_t *root, const char *path, int flags, if (stbuf) { if (is_root) { + daos_epoch_t ep; + + /** refresh possibly stale root stbuf */ + rc = fetch_entry(dfs->layout_v, dfs->super_oh, DAOS_TX_NONE, "/", 1, false, + &exists, &entry, 0, NULL, NULL, NULL); + if (rc) { + D_ERROR("fetch_entry() failed: %d (%s)\n", rc, strerror(rc)); + D_GOTO(err_obj, rc); + } + + if (!exists || !S_ISDIR(entry.mode)) { + /** something really bad happened! */ + D_ERROR("Root object corrupted!"); + D_GOTO(err_obj, rc = EIO); + } + + if (mode) + *mode = entry.mode; + dfs->root_stbuf.st_mode = entry.mode; + dfs->root_stbuf.st_uid = entry.uid; + dfs->root_stbuf.st_gid = entry.gid; + + rc = daos_obj_query_max_epoch(dfs->root.oh, DAOS_TX_NONE, &ep, NULL); + if (rc) + D_GOTO(err_obj, rc = daos_der2errno(rc)); + + /** object was updated since creation */ + rc = update_stbuf_times(entry, ep, &dfs->root_stbuf, NULL); + if (rc) + D_GOTO(err_obj, rc); + if (tspec_gt(dfs->root_stbuf.st_ctim, dfs->root_stbuf.st_mtim)) { + dfs->root_stbuf.st_atim.tv_sec = entry.ctime; + dfs->root_stbuf.st_atim.tv_nsec = entry.ctime_nano; + } else { + dfs->root_stbuf.st_atim.tv_sec = entry.mtime; + dfs->root_stbuf.st_atim.tv_nsec = entry.mtime_nano; + } memcpy(stbuf, &dfs->root_stbuf, sizeof(struct stat)); } else { stbuf->st_nlink = 1; stbuf->st_mode = obj->mode; stbuf->st_uid = entry.uid; - stbuf->st_gid = entry.gid; - stbuf->st_mtim.tv_sec = entry.mtime; - stbuf->st_mtim.tv_nsec = entry.mtime_nano; - stbuf->st_ctim.tv_sec = entry.ctime; - stbuf->st_ctim.tv_nsec = entry.ctime_nano; + stbuf->st_gid = entry.gid; if (tspec_gt(stbuf->st_ctim, stbuf->st_mtim)) { stbuf->st_atim.tv_sec = entry.ctime; stbuf->st_atim.tv_nsec = entry.ctime_nano; @@ -5408,11 +5460,12 @@ dfs_osetattr(dfs_t *dfs, dfs_obj_t *obj, struct stat *stbuf, int flags) bool set_size = false; bool set_mtime = false; bool set_ctime = false; - int i = 0; - size_t len; - int rc; + int i = 0, hlc_recx_idx = 0; + size_t len; uint64_t obj_hlc = 0; struct stat rstat = {}; + daos_array_stbuf_t array_stbuf = {0}; + int rc; if (dfs == NULL || !dfs->mounted) return EINVAL; @@ -5509,6 +5562,10 @@ dfs_osetattr(dfs_t *dfs, dfs_obj_t *obj, struct stat *stbuf, int flags) d_iov_set(&sg_iovs[i], &obj_hlc, sizeof(uint64_t)); recxs[i].rx_idx = HLC_IDX; recxs[i].rx_nr = sizeof(uint64_t); + if (flags & DFS_SET_ATTR_SIZE) { + /** we need to update this again after the set size */ + hlc_recx_idx = i; + } i++; set_mtime = true; @@ -5558,38 +5615,41 @@ dfs_osetattr(dfs_t *dfs, dfs_obj_t *obj, struct stat *stbuf, int flags) rstat.st_blocks = (stbuf->st_size + (1 << 9) - 1) >> 9; rstat.st_size = stbuf->st_size; - /* mtime and ctime need to be updated too only if not set earlier */ - if (!set_mtime || !set_ctime) { - daos_array_stbuf_t array_stbuf = {0}; + /** + * if mtime is set, we need to to just update the hlc on the entry. if mtime and/or + * ctime were not set, we need to update the stat buf returned. both cases require + * an array stat for the hlc. + */ + /** TODO - need an array API to just stat the max epoch without size */ + rc = daos_array_stat(obj->oh, th, &array_stbuf, NULL); + if (rc) + D_GOTO(out_obj, rc = daos_der2errno(rc)); - /** TODO - need an array API to just stat the max epoch without size */ - rc = daos_array_stat(obj->oh, th, &array_stbuf, NULL); - if (rc) + if (!set_mtime) { + rc = d_hlc2timespec(array_stbuf.st_max_epoch, &rstat.st_mtim); + if (rc) { + D_ERROR("d_hlc2timespec() failed " DF_RC "\n", DP_RC(rc)); D_GOTO(out_obj, rc = daos_der2errno(rc)); - - if (!set_mtime) { - rc = d_hlc2timespec(array_stbuf.st_max_epoch, &rstat.st_mtim); - if (rc) { - D_ERROR("d_hlc2timespec() failed "DF_RC"\n", DP_RC(rc)); - D_GOTO(out_obj, rc = daos_der2errno(rc)); - } } + } else { + D_ASSERT(hlc_recx_idx > 0); + D_ASSERT(recxs[hlc_recx_idx].rx_idx == HLC_IDX); + d_iov_set(&sg_iovs[hlc_recx_idx], &array_stbuf.st_max_epoch, + sizeof(uint64_t)); + } - if (!set_ctime) { - rc = d_hlc2timespec(array_stbuf.st_max_epoch, &rstat.st_ctim); - if (rc) { - D_ERROR("d_hlc2timespec() failed "DF_RC"\n", DP_RC(rc)); - D_GOTO(out_obj, rc = daos_der2errno(rc)); - } + if (!set_ctime) { + rc = d_hlc2timespec(array_stbuf.st_max_epoch, &rstat.st_ctim); + if (rc) { + D_ERROR("d_hlc2timespec() failed " DF_RC "\n", DP_RC(rc)); + D_GOTO(out_obj, rc = daos_der2errno(rc)); } } } iod.iod_nr = i; - if (i == 0) D_GOTO(out_stat, rc = 0); - sgl.sg_nr = i; sgl.sg_nr_out = 0; sgl.sg_iovs = &sg_iovs[0]; @@ -6686,7 +6746,7 @@ oit_mark_cb(dfs_t *dfs, dfs_obj_t *parent, const char name[], void *args) } /** open the entry name and get the oid */ - rc = dfs_lookup_rel(dfs, parent, name, O_RDONLY, &obj, NULL, NULL); + rc = dfs_lookup_rel(dfs, parent, name, O_RDONLY | O_NOFOLLOW, &obj, NULL, NULL); if (rc) { D_ERROR("dfs_lookup_rel() of %s failed: %d\n", name, rc); return rc; diff --git a/src/client/dfs/duns.c b/src/client/dfs/duns.c index fadd2f1f769..9f262857e6f 100644 --- a/src/client/dfs/duns.c +++ b/src/client/dfs/duns.c @@ -1331,14 +1331,6 @@ duns_destroy_path(daos_handle_t poh, const char *path) return rc; } - /** Destroy the container */ - rc = daos_cont_destroy(poh, dattr.da_cont, 1, NULL); - if (rc) { - D_ERROR("Failed to destroy container (%d)\n", rc); - /** recreate the link ? */ - return daos_der2errno(rc); - } - if (dattr.da_type == DAOS_PROP_CO_LAYOUT_POSIX) { #ifdef LUSTRE_INCLUDE if (dattr.da_on_lustre) @@ -1369,6 +1361,14 @@ duns_destroy_path(daos_handle_t poh, const char *path) } } + /** Destroy the container */ + rc = daos_cont_destroy(poh, dattr.da_cont, 1, NULL); + if (rc) { + D_ERROR("Failed to destroy container (%d)\n", rc); + /** recreate the link ? */ + return daos_der2errno(rc); + } + return 0; } diff --git a/src/container/oid_iv.c b/src/container/oid_iv.c index d1041184006..f10f5d34f7e 100644 --- a/src/container/oid_iv.c +++ b/src/container/oid_iv.c @@ -31,6 +31,7 @@ struct oid_iv_entry { struct oid_iv_range rg; /** protect the entry */ ABT_mutex lock; + void *current_req; }; /** Priv data in the iv layer */ @@ -130,7 +131,14 @@ oid_iv_ent_update(struct ds_iv_entry *ns_entry, struct ds_iv_key *iv_key, D_ASSERT(priv != NULL); entry = ns_entry->iv_value.sg_iovs[0].iov_buf; - ABT_mutex_lock(entry->lock); + rc = ABT_mutex_trylock(entry->lock); + /* For retry requests, from _iv_op(), the lock may not be released + * in some cases. + */ + if (rc == ABT_ERR_MUTEX_LOCKED && entry->current_req != src) + return -DER_BUSY; + + entry->current_req = src; avail = &entry->rg; oids = src->sg_iovs[0].iov_buf; diff --git a/src/control/cmd/daos/filesystem.go b/src/control/cmd/daos/filesystem.go index e7fcf828a45..8baa1c93535 100644 --- a/src/control/cmd/daos/filesystem.go +++ b/src/control/cmd/daos/filesystem.go @@ -291,7 +291,6 @@ type fsCheckCmd struct { FsckFlags FsCheckFlag `long:"flags" short:"f" description:"comma-separated flags: print, remove, relink, verify, evict"` DirName string `long:"dir-name" short:"n" description:"directory name under lost+found to store leaked oids (a timestamp dir would be created if this is not specified)"` - Evict bool `long:"evict" short:"e" description:"evict all open handles on the container"` } func (cmd *fsCheckCmd) Execute(_ []string) error { diff --git a/src/control/lib/control/pool.go b/src/control/lib/control/pool.go index 79b1efaf1e2..24327914275 100644 --- a/src/control/lib/control/pool.go +++ b/src/control/lib/control/pool.go @@ -1365,7 +1365,7 @@ func processNVMeSpaceStats(log debugLogger, filterRank filterRankFn, nvmeControl for _, smdDevice := range controller.SmdDevices { if !smdDevice.Roles.IsEmpty() && (smdDevice.Roles.OptionBits&storage.BdevRoleData) == 0 { log.Debugf("Skipping SMD device %s (rank %d, ctrlr %s) not used for storing data", - smdDevice.UUID, smdDevice.Rank, controller.PciAddr, smdDevice.Rank) + smdDevice.UUID, smdDevice.Rank, controller.PciAddr) continue } @@ -1377,7 +1377,7 @@ func processNVMeSpaceStats(log debugLogger, filterRank filterRankFn, nvmeControl if !filterRank(smdDevice.Rank) { log.Debugf("Skipping SMD device %s (rank %d, ctrlr %s) not in ranklist", - smdDevice.UUID, smdDevice.Rank, controller.PciAddr, smdDevice.Rank) + smdDevice.UUID, smdDevice.Rank, controller.PciAddr) continue } diff --git a/src/engine/server_iv.c b/src/engine/server_iv.c index a7d258705a3..5f5d00722cc 100644 --- a/src/engine/server_iv.c +++ b/src/engine/server_iv.c @@ -1,5 +1,5 @@ /** - * (C) Copyright 2017-2023 Intel Corporation. + * (C) Copyright 2017-2024 Intel Corporation. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -1053,7 +1053,7 @@ _iv_op(struct ds_iv_ns *ns, struct ds_iv_key *key, d_sg_list_t *value, retry: rc = iv_op_internal(ns, key, value, sync, shortcut, opc); if (retry && !ns->iv_stop && - (daos_rpc_retryable_rc(rc) || rc == -DER_NOTLEADER)) { + (daos_rpc_retryable_rc(rc) || rc == -DER_NOTLEADER || rc == -DER_BUSY)) { if (rc == -DER_NOTLEADER && key->rank != (d_rank_t)(-1) && sync && (sync->ivs_mode == CRT_IV_SYNC_LAZY || sync->ivs_mode == CRT_IV_SYNC_EAGER)) { @@ -1070,7 +1070,7 @@ _iv_op(struct ds_iv_ns *ns, struct ds_iv_key *key, d_sg_list_t *value, * but in-flight fetch request return IVCB_FORWARD, then queued RPC will * reply IVCB_FORWARD. */ - D_WARN("ns %u retry for class %d opc %d rank %u/%u: " DF_RC "\n", ns->iv_ns_id, + D_INFO("ns %u retry for class %d opc %d rank %u/%u: " DF_RC "\n", ns->iv_ns_id, key->class_id, opc, key->rank, ns->iv_master_rank, DP_RC(rc)); /* sleep 1sec and retry */ dss_sleep(1000); diff --git a/src/gurt/misc.c b/src/gurt/misc.c index de0a1ae6fd7..3b287ca73ff 100644 --- a/src/gurt/misc.c +++ b/src/gurt/misc.c @@ -25,8 +25,6 @@ #include #include -#define UINT64_MAX_STR "18446744073709551615" - /* state buffer for DAOS rand and srand calls, NOT thread safe */ static struct drand48_data randBuffer = {0}; @@ -951,18 +949,17 @@ d_rank_range_list_free(d_rank_range_list_t *range_list) } static inline bool -dis_unsigned_str(char *str) +dis_signed_str(char *str) { - char *eos; - - if (str == NULL || str[0] == '\0') - return false; + char *eos; + size_t str_size; - eos = str + (sizeof(UINT64_MAX_STR) - 1); - while (str != eos && *str != '\0' && *str >= '0' && *str <= '9') + str_size = strlen(str); + eos = str + str_size; + while (str != eos && *str != '-' && (*str < '0' || *str > '9')) ++str; - return *str == '\0'; + return *str == '-'; } static inline bool @@ -1214,40 +1211,68 @@ d_getenv_char(const char *name, char *char_val) } static int -d_getenv_ull(unsigned long long *val, const char *name) +d_getenv_ull(unsigned long long *val, const char *name, size_t val_size) { char *env; + char *env_tmp = NULL; char *endptr; - unsigned long long tmp; + unsigned long long val_tmp; int rc; assert(val != NULL); assert(name != NULL); + assert(val_size <= sizeof(unsigned long long)); d_env_rwlock_rdlock(); env = getenv(name); if (env == NULL) { rc = -DER_NONEXIST; + d_env_rwlock_unlock(); goto out; } - if (!dis_unsigned_str(env)) { - rc = -DER_INVAL; + /* DAOS-14896 NOTES: + * - Duplicate env to reduce data race condition with external libraries not using the DAOS + * thread safe environment variables management API. + * - Use of strdup() as there is no limit to environment variable size. + */ + env_tmp = strdup(env); + if (env_tmp == NULL) { + rc = -DER_NOMEM; + d_env_rwlock_unlock(); goto out; } + d_env_rwlock_unlock(); - errno = 0; - tmp = strtoull(env, &endptr, 0); - if (errno != 0 || endptr == env || *endptr != '\0') { + errno = 0; + val_tmp = strtoull(env_tmp, &endptr, 10); + if (errno != 0 || endptr == env_tmp || *endptr != '\0') { rc = -DER_INVAL; goto out; } - *val = tmp; + if (val_size != sizeof(unsigned long long)) { + const unsigned long long val_max = (1ull << val_size * 8) - 1; + const bool is_signed = dis_signed_str(env_tmp); + + if (is_signed) + val_tmp = ~val_tmp; + if (val_tmp > val_max || (is_signed && val_tmp >= val_max)) { + rc = -DER_INVAL; + goto out; + } + if (is_signed) { + val_tmp = ~val_tmp; + val_tmp <<= (sizeof(unsigned long long) - val_size) * 8; + val_tmp >>= (sizeof(unsigned long long) - val_size) * 8; + } + } + + *val = val_tmp; rc = -DER_SUCCESS; out: - d_env_rwlock_unlock(); + free(env_tmp); return rc; } @@ -1269,21 +1294,29 @@ d_getenv_uint(const char *name, unsigned *uint_val) assert(uint_val != NULL); assert(name != NULL); - rc = d_getenv_ull(&tmp, name); + rc = d_getenv_ull(&tmp, name, sizeof(unsigned)); if (rc != -DER_SUCCESS) return rc; -#if UINT_MAX != ULLONG_MAX - assert(sizeof(unsigned) < sizeof(unsigned long long)); - if (tmp > UINT_MAX) { - return -DER_INVAL; - } -#endif - *uint_val = (unsigned)tmp; return -DER_SUCCESS; } +/** + * get an unsigned integer type environment variables. + * + * \param[in] name name of the environment variable. + * \param[in,out] uint_val returned value of the ENV. Will not change the original + * value if ENV is not set or set as a non-integer value. + * \return 0 on success, a negative value on error. + * \deprecated d_getenv_int() is deprecated, please use d_getenv_uint(). + */ +int +d_getenv_int(const char *name, unsigned *uint_val) +{ + return d_getenv_uint(name, uint_val); +} + /** * get a 32bits unsigned integer type environment variables * @@ -1301,17 +1334,10 @@ d_getenv_uint32_t(const char *name, uint32_t *uint32_val) assert(uint32_val != NULL); assert(name != NULL); - rc = d_getenv_ull(&tmp, name); + rc = d_getenv_ull(&tmp, name, sizeof(uint32_t)); if (rc != -DER_SUCCESS) return rc; -#if UINT32_MAX != ULLONG_MAX - assert(sizeof(uint32_t) < sizeof(unsigned long long)); - if (tmp > UINT32_MAX) { - return -DER_INVAL; - } -#endif - *uint32_val = (uint32_t)tmp; return -DER_SUCCESS; } @@ -1333,17 +1359,10 @@ d_getenv_uint64_t(const char *name, uint64_t *uint64_val) assert(uint64_val != NULL); assert(name != NULL); - rc = d_getenv_ull(&tmp, name); + rc = d_getenv_ull(&tmp, name, sizeof(uint64_t)); if (rc != -DER_SUCCESS) return rc; -#if UINT64_MAX != ULLONG_MAX - assert(sizeof(uint64_t) < sizeof(unsigned long long)); - if (tmp > UINT64_MAX) { - return -DER_INVAL; - } -#endif - *uint64_val = (uint64_t)tmp; return -DER_SUCCESS; } diff --git a/src/gurt/tests/test_gurt.c b/src/gurt/tests/test_gurt.c index 49db0a883e6..ebb9a0ec701 100644 --- a/src/gurt/tests/test_gurt.c +++ b/src/gurt/tests/test_gurt.c @@ -2288,35 +2288,63 @@ test_d_getenv_uint(void **state) assert_int_equal(rc, -DER_SUCCESS); assert_true(val == UINT_MAX); - getenv_return = "42"; + getenv_return = "-1"; + rc = d_getenv_uint("foo", &val); + assert_int_equal(rc, -DER_SUCCESS); + assert_true(val == UINT_MAX); + + getenv_return = "-10"; + rc = d_getenv_uint("foo", &val); + assert_int_equal(rc, -DER_SUCCESS); + assert_true(val == UINT_MAX - 9); + + getenv_return = "-4294967294"; + rc = d_getenv_uint("foo", &val); + assert_true(val == 2); + + getenv_return = "-4294967295"; + rc = d_getenv_uint("foo", &val); + assert_true(val == 1); + + getenv_return = " 000042"; rc = d_getenv_uint("foo", &val); assert_int_equal(rc, -DER_SUCCESS); assert_true(val == 42); + getenv_return = " -000042"; + rc = d_getenv_uint("foo", &val); + assert_int_equal(rc, -DER_SUCCESS); + assert_true(val == -42); + getenv_return = "4294967296"; rc = d_getenv_uint("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); - getenv_return = "-42"; + getenv_return = "-4294967296"; rc = d_getenv_uint("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); getenv_return = "booo"; rc = d_getenv_uint("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); getenv_return = "42booo"; rc = d_getenv_uint("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); + + getenv_return = ""; + rc = d_getenv_uint("foo", &val); + assert_int_equal(rc, -DER_INVAL); + assert_true(val == -42); getenv_return = NULL; rc = d_getenv_uint("foo", &val); assert_int_equal(rc, -DER_NONEXIST); - assert_true(val == 42); + assert_true(val == -42); } static void @@ -2330,40 +2358,63 @@ test_d_getenv_uint32_t(void **state) assert_int_equal(rc, -DER_SUCCESS); assert_true(val == UINT32_MAX); - getenv_return = "42"; + getenv_return = "-1"; + rc = d_getenv_uint32_t("foo", &val); + assert_int_equal(rc, -DER_SUCCESS); + assert_true(val == UINT32_MAX); + + getenv_return = "-10"; + rc = d_getenv_uint32_t("foo", &val); + assert_int_equal(rc, -DER_SUCCESS); + assert_true(val == UINT32_MAX - 9); + + getenv_return = "-4294967294"; + rc = d_getenv_uint32_t("foo", &val); + assert_true(val == 2); + + getenv_return = "-4294967295"; + rc = d_getenv_uint32_t("foo", &val); + assert_true(val == 1); + + getenv_return = " 000042"; rc = d_getenv_uint32_t("foo", &val); assert_int_equal(rc, -DER_SUCCESS); assert_true(val == 42); + getenv_return = " -000042"; + rc = d_getenv_uint32_t("foo", &val); + assert_int_equal(rc, -DER_SUCCESS); + assert_true(val == -42); + getenv_return = "4294967296"; rc = d_getenv_uint32_t("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); - getenv_return = "-42"; + getenv_return = "-4294967296"; rc = d_getenv_uint32_t("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); getenv_return = "booo"; rc = d_getenv_uint32_t("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); getenv_return = "42booo"; rc = d_getenv_uint32_t("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); getenv_return = ""; rc = d_getenv_uint32_t("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); getenv_return = NULL; rc = d_getenv_uint32_t("foo", &val); assert_int_equal(rc, -DER_NONEXIST); - assert_true(val == 42); + assert_true(val == -42); } static void @@ -2377,45 +2428,63 @@ test_d_getenv_uint64_t(void **state) assert_int_equal(rc, -DER_SUCCESS); assert_true(val == UINT64_MAX); - getenv_return = "42"; + getenv_return = "-1"; rc = d_getenv_uint64_t("foo", &val); assert_int_equal(rc, -DER_SUCCESS); - assert_true(val == 42); + assert_true(val == UINT64_MAX); - getenv_return = "18446744073709551616"; + getenv_return = "-10"; rc = d_getenv_uint64_t("foo", &val); - assert_int_equal(rc, -DER_INVAL); + assert_int_equal(rc, -DER_SUCCESS); + assert_true(val == UINT64_MAX - 9); + + getenv_return = "-18446744073709551614"; + rc = d_getenv_uint64_t("foo", &val); + assert_true(val == 2); + + getenv_return = "-18446744073709551615"; + rc = d_getenv_uint64_t("foo", &val); + assert_true(val == 1); + + getenv_return = " 000042"; + rc = d_getenv_uint64_t("foo", &val); + assert_int_equal(rc, -DER_SUCCESS); assert_true(val == 42); - getenv_return = "012345678901234567890"; + getenv_return = " -000042"; + rc = d_getenv_uint64_t("foo", &val); + assert_int_equal(rc, -DER_SUCCESS); + assert_true(val == -42); + + getenv_return = "18446744073709551616"; rc = d_getenv_uint64_t("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); - getenv_return = "-42"; + getenv_return = "-18446744073709551616"; rc = d_getenv_uint64_t("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); getenv_return = "booo"; rc = d_getenv_uint64_t("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); getenv_return = "42booo"; rc = d_getenv_uint64_t("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); getenv_return = ""; rc = d_getenv_uint64_t("foo", &val); assert_int_equal(rc, -DER_INVAL); - assert_true(val == 42); + assert_true(val == -42); getenv_return = NULL; rc = d_getenv_uint64_t("foo", &val); assert_int_equal(rc, -DER_NONEXIST); - assert_true(val == 42); + assert_true(val == -42); } static void diff --git a/src/include/gurt/common.h b/src/include/gurt/common.h index 779a547768b..1cf40fc3292 100644 --- a/src/include/gurt/common.h +++ b/src/include/gurt/common.h @@ -587,6 +587,9 @@ d_getenv_bool(const char *name, bool *bool_val); int d_getenv_char(const char *name, char *char_val); int +d_getenv_int(const char *name, unsigned int *uint_val) + __attribute__((deprecated("use d_getenv_uint"))); +int d_getenv_uint(const char *name, unsigned int *uint_val); int d_getenv_uint32_t(const char *name, uint32_t *uint32_val); @@ -601,13 +604,6 @@ d_unsetenv(const char *name); int d_clearenv(void); -static inline int -d_getenv_int(const char *name, unsigned int *uint_val) -{ - D_WARN("d_getenv_int() is deprecated, please use d_getenv_uint()"); - return d_getenv_uint(name, uint_val); -} - int d_write_string_buffer(struct d_string_buffer_t *buf, const char *fmt, ...); void diff --git a/src/pool/srv_internal.h b/src/pool/srv_internal.h index 0d9854fd436..f997d299f80 100644 --- a/src/pool/srv_internal.h +++ b/src/pool/srv_internal.h @@ -20,7 +20,7 @@ #define POOL_GROUP_MAP_STATES (PO_COMP_ST_UP | PO_COMP_ST_UPIN | PO_COMP_ST_DRAIN) /* Map states of ranks that make up the pool service */ -#define POOL_SVC_MAP_STATES (PO_COMP_ST_UP | PO_COMP_ST_UPIN) +#define POOL_SVC_MAP_STATES (PO_COMP_ST_UPIN) /* * Since we want all PS replicas to belong to the pool group, @@ -255,10 +255,6 @@ int ds_pool_iv_srv_hdl_invalidate(struct ds_pool *pool); int ds_pool_iv_conn_hdl_fetch(struct ds_pool *pool); int ds_pool_iv_conn_hdl_invalidate(struct ds_pool *pool, uuid_t hdl_uuid); -int ds_pool_iv_srv_hdl_fetch_non_sys(struct ds_pool *pool, - uuid_t *srv_cont_hdl, - uuid_t *srv_pool_hdl); - /* * srv_metrics.c */ diff --git a/src/pool/srv_iv.c b/src/pool/srv_iv.c index 92970ff3d5f..a1969c67bd6 100644 --- a/src/pool/srv_iv.c +++ b/src/pool/srv_iv.c @@ -1496,63 +1496,6 @@ ds_pool_iv_srv_hdl_fetch(struct ds_pool *pool, uuid_t *pool_hdl_uuid, return rc; } -struct srv_hdl_ult_arg { - struct ds_pool *pool; - ABT_eventual eventual; -}; - -static void -pool_iv_srv_hdl_fetch_ult(void *data) -{ - struct srv_hdl_ult_arg *arg = data; - int rc; - - rc = ds_pool_iv_srv_hdl_fetch(arg->pool, NULL, NULL); - - ABT_eventual_set(arg->eventual, (void *)&rc, sizeof(rc)); -} - -int -ds_pool_iv_srv_hdl_fetch_non_sys(struct ds_pool *pool, uuid_t *srv_cont_hdl, - uuid_t *srv_pool_hdl) -{ - struct srv_hdl_ult_arg arg; - ABT_eventual eventual; - int *status; - int rc; - - /* Fetch the capability from the leader. To avoid extra locks, - * all metadatas are maintained by xstream 0, so let's create - * an ULT on xstream 0 to let xstream 0 to handle capa fetch - * and update. - */ - rc = ABT_eventual_create(sizeof(*status), &eventual); - if (rc != ABT_SUCCESS) - return dss_abterr2der(rc); - - arg.pool = pool; - arg.eventual = eventual; - rc = dss_ult_create(pool_iv_srv_hdl_fetch_ult, &arg, DSS_XS_SYS, - 0, 0, NULL); - if (rc) - D_GOTO(out_eventual, rc); - - rc = ABT_eventual_wait(eventual, (void **)&status); - if (rc != ABT_SUCCESS) - D_GOTO(out_eventual, rc = dss_abterr2der(rc)); - if (*status != 0) - D_GOTO(out_eventual, rc = *status); - - if (srv_cont_hdl) - uuid_copy(*srv_cont_hdl, pool->sp_srv_cont_hdl); - if (srv_pool_hdl) - uuid_copy(*srv_pool_hdl, pool->sp_srv_pool_hdl); - -out_eventual: - ABT_eventual_free(&eventual); - return rc; -} - int ds_pool_iv_prop_update(struct ds_pool *pool, daos_prop_t *prop) { diff --git a/src/pool/srv_pool.c b/src/pool/srv_pool.c index 6396e6bea59..8f857ea1e12 100644 --- a/src/pool/srv_pool.c +++ b/src/pool/srv_pool.c @@ -1,5 +1,5 @@ /* - * (C) Copyright 2016-2023 Intel Corporation. + * (C) Copyright 2016-2024 Intel Corporation. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -1810,6 +1810,11 @@ pool_svc_step_up_cb(struct ds_rsvc *rsvc) } else { uuid_generate(pool_hdl_uuid); uuid_generate(cont_hdl_uuid); + /* Only copy server handle to make is_from_srv() check correctly, and + * container server handle will not be copied here, otherwise + * ds_pool_iv_refresh_hdl will not open the server container handle. + */ + uuid_copy(svc->ps_pool->sp_srv_pool_hdl, pool_hdl_uuid); } rc = ds_pool_iv_srv_hdl_update(svc->ps_pool, pool_hdl_uuid, @@ -4296,8 +4301,7 @@ ds_pool_query_handler(crt_rpc_t *rpc, int handler_version) metrics = svc->ps_pool->sp_metrics[DAOS_POOL_MODULE]; /* See comment above, rebuild doesn't connect the pool */ - if ((query_bits & DAOS_PO_QUERY_SPACE) && - !is_pool_from_srv(in->pqi_op.pi_uuid, in->pqi_op.pi_hdl)) { + if (query_bits & DAOS_PO_QUERY_SPACE) { rc = pool_space_query_bcast(rpc->cr_ctx, svc, in->pqi_op.pi_hdl, &out->pqo_space); if (unlikely(rc)) diff --git a/src/rdb/rdb.c b/src/rdb/rdb.c index f5c3eb47629..a1d39c507ef 100644 --- a/src/rdb/rdb.c +++ b/src/rdb/rdb.c @@ -684,7 +684,7 @@ rdb_resign(struct rdb *db, uint64_t term) * * \param[in] db database * - * \retval -DER_INVAL not a voting replica + * \retval -DER_NO_PERM not a voting replica or might violate a lease */ int rdb_campaign(struct rdb *db) diff --git a/src/rdb/rdb_raft.c b/src/rdb/rdb_raft.c index 8be8dc6ed99..886a873729c 100644 --- a/src/rdb/rdb_raft.c +++ b/src/rdb/rdb_raft.c @@ -61,6 +61,7 @@ rdb_raft_rc(int raft_rc) case RAFT_ERR_NOMEM: return -DER_NOMEM; case RAFT_ERR_SNAPSHOT_ALREADY_LOADED: return -DER_ALREADY; case RAFT_ERR_INVALID_CFG_CHANGE: return -DER_INVAL; + case RAFT_ERR_MIGHT_VIOLATE_LEASE: return -DER_NO_PERM; default: return -DER_MISC; } } @@ -2854,7 +2855,7 @@ rdb_raft_campaign(struct rdb *db) node = raft_get_my_node(db->d_raft); if (node == NULL || !raft_node_is_voting(node)) { D_DEBUG(DB_MD, DF_DB": must be voting node\n", DP_DB(db)); - rc = -DER_INVAL; + rc = -DER_NO_PERM; goto out_mutex; } diff --git a/src/rebuild/scan.c b/src/rebuild/scan.c index 4c2c78c4bee..352459a2d84 100644 --- a/src/rebuild/scan.c +++ b/src/rebuild/scan.c @@ -1,5 +1,5 @@ /** - * (C) Copyright 2017-2023 Intel Corporation. + * (C) Copyright 2017-2024 Intel Corporation. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -882,12 +882,13 @@ rebuild_container_scan_cb(daos_handle_t ih, vos_iter_entry_t *entry, } /* Wait for EC aggregation to finish. NB: migrate needs to wait for EC aggregation to finish */ - while (cont_child->sc_ec_agg_active) { + while (cont_child->sc_ec_agg_active && + rpt->rt_rebuild_op != RB_OP_RECLAIM && + rpt->rt_rebuild_op != RB_OP_FAIL_RECLAIM) { D_ASSERTF(rpt->rt_pool->sp_rebuilding >= 0, DF_UUID" rebuilding %d\n", DP_UUID(rpt->rt_pool_uuid), rpt->rt_pool->sp_rebuilding); /* Wait for EC aggregation to abort before discard the object */ - D_DEBUG(DB_REBUILD, DF_UUID" wait for ec agg abort.\n", - DP_UUID(entry->ie_couuid)); + D_INFO(DF_UUID" wait for ec agg abort.\n", DP_UUID(entry->ie_couuid)); dss_sleep(1000); if (rpt->rt_abort || rpt->rt_finishing) { D_DEBUG(DB_REBUILD, DF_CONT" rebuild op %s ver %u abort %u/%u.\n", diff --git a/src/tests/ftest/daos_test/suite.yaml b/src/tests/ftest/daos_test/suite.yaml index afcc048f965..5d08fb4493d 100644 --- a/src/tests/ftest/daos_test/suite.yaml +++ b/src/tests/ftest/daos_test/suite.yaml @@ -191,3 +191,5 @@ daos_tests: test_daos_extend_simple: 5 test_daos_rebuild_ec: 43 test_daos_degraded_ec: 29 + crt_timeout: + test_daos_oid_allocator: 60 diff --git a/src/tests/ftest/erasurecode/multiple_failure.yaml b/src/tests/ftest/erasurecode/multiple_failure.yaml index dbd63f69bbe..73ceb3bfdc0 100644 --- a/src/tests/ftest/erasurecode/multiple_failure.yaml +++ b/src/tests/ftest/erasurecode/multiple_failure.yaml @@ -25,7 +25,7 @@ server_config: storage: auto pool: size: 93% - svcn: 1 + svcn: 5 control_method: dmg container: type: POSIX diff --git a/src/tests/ftest/erasurecode/online_rebuild_single.yaml b/src/tests/ftest/erasurecode/online_rebuild_single.yaml index bda14dbb9e5..8b0b3f4baf0 100644 --- a/src/tests/ftest/erasurecode/online_rebuild_single.yaml +++ b/src/tests/ftest/erasurecode/online_rebuild_single.yaml @@ -30,9 +30,10 @@ server_config: storage: auto pool: size: 93% - svcn: 1 + svcn: 5 control_method: dmg pool_query_timeout: 30 + properties: rd_fac:2 container: type: POSIX control_method: API diff --git a/src/tests/ftest/ior/hard_rebuild.yaml b/src/tests/ftest/ior/hard_rebuild.yaml index a3a5f5f5444..97137c97b46 100644 --- a/src/tests/ftest/ior/hard_rebuild.yaml +++ b/src/tests/ftest/ior/hard_rebuild.yaml @@ -31,34 +31,28 @@ server_config: log_file: daos_server1.log log_mask: ERR storage: auto -create_pool_max_size: - scm: true - percentage: 90 pool: - control_method: dmg + size: 90% container: type: POSIX control_method: daos - properties: dedup:memcmp ior: api: "DFS" client_processes: np: 32 - dfs_destroy: false iorflags: flags: "-C -k -e -w -g -G 27 -D 120 -Q 1 -vv" read_flags: "-C -k -e -r -R -g -G 27 -D 120 -Q 1 -vv" test_file: daos:testFile segment_count: 2000000 - repetitions: 1 chunk_block_transfer_sizes: # [ChunkSize, BlocksSize, TransferSize] - [47008, 47008, 47008] objectclass: dfs_oclass_list: # - [EC_Object_Class, Minimum number of servers] - - ["EC_2P2G1", 6] - - ["EC_4P2G1", 8] - - ["EC_8P2G1", 12] + - ["EC_2P2GX", 6] + - ["EC_4P2GX", 8] + - ["EC_8P2GX", 12] sw_wearout: 1 sw_status_file: "/var/tmp/daos_testing/stoneWallingStatusFile" diff --git a/src/tests/ftest/util/daos_core_base.py b/src/tests/ftest/util/daos_core_base.py index 9bf0ff4c501..1baa93b91b4 100644 --- a/src/tests/ftest/util/daos_core_base.py +++ b/src/tests/ftest/util/daos_core_base.py @@ -1,5 +1,5 @@ """ - (C) Copyright 2018-2023 Intel Corporation. + (C) Copyright 2018-2024 Intel Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent """ @@ -96,6 +96,14 @@ def start_server_managers(self, force=False): ["=".join(items) for items in list(env_dict.items())] ) + # Update any other server settings unique to this test method + for setting in ["crt_timeout"]: + value = self.get_test_param(setting) + if value: + for server_mgr in self.server_managers: + for engine_params in server_mgr.manager.job.yaml.engine_params: + engine_params.set_value(setting, value) + # Start the servers return super().start_server_managers(force=force) diff --git a/src/tests/suite/dfs_test.c b/src/tests/suite/dfs_test.c index f35e9df0f7b..217f30ad178 100644 --- a/src/tests/suite/dfs_test.c +++ b/src/tests/suite/dfs_test.c @@ -1,5 +1,5 @@ /** - * (C) Copyright 2019-2022 Intel Corporation. + * (C) Copyright 2019-2024 Intel Corporation. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -81,16 +81,17 @@ run_specified_tests(const char *tests, int rank, int size, int main(int argc, char **argv) { - test_arg_t *arg; - char tests[64]; - char *exclude_str = NULL; - int ntests = 0; - int nr_failed = 0; - int nr_total_failed = 0; - int opt = 0, index = 0; - int rank; - int size; - int rc; + test_arg_t *arg; + char tests[64]; + char *exclude_str = NULL; + char *cmocka_message_output = NULL; + int ntests = 0; + int nr_failed = 0; + int nr_total_failed = 0; + int opt = 0, index = 0; + int rank; + int size; + int rc; d_register_alt_assert(mock_assert); @@ -166,6 +167,16 @@ main(int argc, char **argv) tests[new_idx] = '\0'; } + /** if writing XML, force all ranks other than rank 0 to use stdout to avoid conflicts */ + cmocka_message_output = getenv("CMOCKA_MESSAGE_OUTPUT"); + if (rank != 0 && cmocka_message_output && strcasecmp(cmocka_message_output, "xml") == 0) { + rc = d_setenv("CMOCKA_MESSAGE_OUTPUT", "stdout", 1); + if (rc) { + print_message("d_setenv() failed with %d\n", rc); + return -1; + } + } + nr_failed = run_specified_tests(tests, rank, size, NULL, 0); exit: diff --git a/src/tests/suite/dfs_unit_test.c b/src/tests/suite/dfs_unit_test.c index 6c2bf8fe1bb..cc3091c3fe3 100644 --- a/src/tests/suite/dfs_unit_test.c +++ b/src/tests/suite/dfs_unit_test.c @@ -1415,15 +1415,45 @@ dfs_test_chown(void **state) char *filename_file2 = "open_stat2"; mode_t create_mode = S_IWUSR | S_IRUSR; int create_flags = O_RDWR | O_CREAT | O_EXCL; + struct timespec ctime_orig, mtime_orig; + mode_t orig_mode; int rc; if (arg->myrank != 0) return; - rc = dfs_lookup(dfs_mt, "/", O_RDWR, &dir, NULL, &stbuf); + rc = dfs_lookup(dfs_mt, "/", O_RDWR, &dir, &orig_mode, &stbuf); assert_int_equal(rc, 0); assert_int_equal(stbuf.st_uid, geteuid()); assert_int_equal(stbuf.st_gid, getegid()); + mtime_orig.tv_sec = stbuf.st_mtim.tv_sec; + mtime_orig.tv_nsec = stbuf.st_mtim.tv_nsec; + ctime_orig.tv_sec = stbuf.st_ctim.tv_sec; + ctime_orig.tv_nsec = stbuf.st_ctim.tv_nsec; + + /** chown of root and see if visible */ + print_message("Running chown tests on root object...\n"); + memset(&stbuf, 0, sizeof(stbuf)); + stbuf.st_uid = 3; + stbuf.st_gid = 4; + stbuf.st_mtim.tv_sec = mtime_orig.tv_sec + 10; + stbuf.st_mtim.tv_nsec = mtime_orig.tv_nsec; + stbuf.st_mode = orig_mode | S_IROTH | S_IWOTH | S_IXOTH; + rc = dfs_osetattr(dfs_mt, dir, &stbuf, + DFS_SET_ATTR_UID | DFS_SET_ATTR_GID | DFS_SET_ATTR_MTIME | + DFS_SET_ATTR_MODE); + assert_int_equal(rc, 0); + rc = dfs_release(dir); + assert_int_equal(rc, 0); + + memset(&stbuf, 0, sizeof(stbuf)); + rc = dfs_lookup(dfs_mt, "/", O_RDWR, &dir, NULL, &stbuf); + assert_int_equal(rc, 0); + assert_int_equal(stbuf.st_mode, orig_mode | S_IROTH | S_IWOTH | S_IXOTH); + assert_int_equal(stbuf.st_uid, 3); + assert_int_equal(stbuf.st_gid, 4); + assert_true(check_ts(ctime_orig, stbuf.st_ctim)); + assert_int_equal(mtime_orig.tv_sec + 10, stbuf.st_mtim.tv_sec); rc = dfs_release(dir); assert_int_equal(rc, 0); @@ -1495,6 +1525,11 @@ run_time_tests(dfs_obj_t *obj, char *name, int mode) struct timespec prev_ts, first_ts; daos_size_t size; dfs_obj_t *tmp_obj; + struct tm tm = {0}; + time_t ts; + char *p; + struct tm *timeptr; + char time_str[64]; int rc; rc = dfs_stat(dfs_mt, NULL, name, &stbuf); @@ -1582,8 +1617,34 @@ run_time_tests(dfs_obj_t *obj, char *name, int mode) prev_ts.tv_sec = stbuf.st_mtim.tv_sec; prev_ts.tv_nsec = stbuf.st_mtim.tv_nsec; - /** set size on file with dfs_osetattr and stat at same time */ if (S_ISREG(mode)) { + /** set mtime and size at the same time; mtime should be what we set */ + memset(&stbuf, 0, sizeof(stbuf)); + stbuf.st_size = 1000; + p = strptime("2023-12-31", "%Y-%m-%d", &tm); + assert_non_null(p); + ts = mktime(&tm); + stbuf.st_mtim.tv_sec = ts; + stbuf.st_mtim.tv_nsec = 0; + rc = dfs_osetattr(dfs_mt, obj, &stbuf, DFS_SET_ATTR_SIZE | DFS_SET_ATTR_MTIME); + assert_int_equal(rc, 0); + assert_int_equal(stbuf.st_size, 1000); + /** check the mtime was updated with the setattr */ + assert_int_equal(ts, stbuf.st_mtim.tv_sec); + timeptr = localtime(&stbuf.st_mtim.tv_sec); + strftime(time_str, sizeof(time_str), "%Y-%m-%d", timeptr); + print_message("mtime = %s\n", time_str); + assert_true(strncmp("2023", time_str, 4) == 0); + + memset(&stbuf, 0, sizeof(stbuf)); + rc = dfs_ostat(dfs_mt, obj, &stbuf); + assert_int_equal(rc, 0); + assert_int_equal(stbuf.st_size, 1000); + timeptr = localtime(&stbuf.st_mtim.tv_sec); + strftime(time_str, sizeof(time_str), "%Y-%m-%d", timeptr); + assert_int_equal(ts, stbuf.st_mtim.tv_sec); + assert_true(strncmp("2023", time_str, 4) == 0); + memset(&stbuf, 0, sizeof(stbuf)); stbuf.st_size = 1024; rc = dfs_osetattr(dfs_mt, obj, &stbuf, DFS_SET_ATTR_SIZE); @@ -1593,12 +1654,6 @@ run_time_tests(dfs_obj_t *obj, char *name, int mode) assert_true(check_ts(prev_ts, stbuf.st_mtim)); } - struct tm tm = {0}; - time_t ts; - char *p; - struct tm *timeptr; - char time_str[64]; - /** set the mtime to 2020 */ p = strptime("2020-12-31", "%Y-%m-%d", &tm); assert_non_null(p); @@ -2503,7 +2558,7 @@ dfs_test_checker(void **state) test_arg_t *arg = *state; dfs_t *dfs; int nr = 100, i; - dfs_obj_t *root, *lf; + dfs_obj_t *root, *lf, *sym; daos_obj_id_t root_oid; daos_handle_t root_oh; daos_handle_t coh; @@ -2574,6 +2629,12 @@ dfs_test_checker(void **state) assert_int_equal(rc, 0); } + /** create a symlink with a non-existent target in the container */ + rc = dfs_open(dfs, NULL, "SL1", S_IFLNK | S_IWUSR | S_IRUSR, O_RDWR | O_CREAT | O_EXCL, 0, + 0, "/usr/local", &sym); + assert_int_equal(rc, 0); + rc = dfs_release(sym); + rc = dfs_disconnect(dfs); assert_int_equal(rc, 0); /** have to call fini to release the cached container handle for the checker to work */ diff --git a/utils/node_local_test.py b/utils/node_local_test.py index 61d254ef9b7..97d48821c40 100755 --- a/utils/node_local_test.py +++ b/utils/node_local_test.py @@ -2838,6 +2838,7 @@ def test_uns_link(self): print(os.listdir(path)) cmd = ['cont', 'destroy', '--path', path] rc = run_daos_cmd(self.conf, cmd) + assert rc.returncode == 0 path = join(self.dfuse.dir, 'uns_link2') cmd = ['cont', 'link', self.pool.id(), container2.id(), '--path', path]