diff --git a/0005-BZ_217095_Modify-structures-in-libfuse-to-handle-flags-beyond-rhel-8.patch b/0005-BZ_217095_Modify-structures-in-libfuse-to-handle-flags-beyond-rhel-8.patch new file mode 100644 index 0000000..236c60f --- /dev/null +++ b/0005-BZ_217095_Modify-structures-in-libfuse-to-handle-flags-beyond-rhel-8.patch @@ -0,0 +1,179 @@ +From 4df08719f3415cde6f802a755922b7f76e198cd7 Mon Sep 17 00:00:00 2001 +From: Dharmendra Singh +Date: Mon, 28 Feb 2022 11:15:06 +0000 +Subject: [PATCH] Modify structures in libfuse to handle flags beyond 32 bits. + +In fuse kernel, 'commit 53db28933e95 ("fuse: extend init flags")' +made the changes to handle flags going beyond 32 bits but i think +changes were not done in libfuse to handle the same. + +This patch prepares the ground in libfuse for incoming FUSE kernel +patches (Atomic open + lookup) where flags went beyond 32 bits. +It makes struct same as in fuse kernel resulting in name change of +few fields. +--- + include/fuse_kernel.h | 8 ++++- + lib/fuse_lowlevel.c | 75 ++++++++++++++++++++++++++++---------------------- + 2 files changed, 48 insertions(+), 35 deletions(-) + +--- a/include/fuse_kernel.h ++++ b/include/fuse_kernel.h +@@ -272,6 +272,7 @@ struct fuse_file_lock { + #define FUSE_POSIX_ACL (1 << 20) + #define FUSE_MAX_PAGES (1 << 22) + #define FUSE_CACHE_SYMLINKS (1 << 23) ++#define FUSE_INIT_EXT (1 << 30) + + /** + * CUSE INIT request/reply flags +@@ -596,6 +597,8 @@ struct fuse_init_in { + uint32_t minor; + uint32_t max_readahead; + uint32_t flags; ++ uint32_t flags2; ++ uint32_t unused[11]; + }; + + #define FUSE_COMPAT_INIT_OUT_SIZE 8 +@@ -611,8 +614,9 @@ struct fuse_init_out { + uint32_t max_write; + uint32_t time_gran; + uint16_t max_pages; +- uint16_t padding; +- uint32_t unused[8]; ++ uint16_t map_alignment; ++ uint32_t flags2; ++ uint32_t unused[7]; + }; + + #define CUSE_INIT_INFO_MAX 4096 +--- a/lib/fuse_lowlevel.c ++++ b/lib/fuse_lowlevel.c +@@ -1819,7 +1819,8 @@ static void do_init(fuse_req_t req, fuse + struct fuse_session *se = req->se; + size_t bufsize = se->bufsize; + size_t outargsize = sizeof(outarg); +- ++ uint64_t inargflags = 0; ++ uint64_t outargflags = 0; + (void) nodeid; + if (se->debug) { + fprintf(stderr, "INIT: %u.%u\n", arg->major, arg->minor); +@@ -1854,39 +1855,42 @@ static void do_init(fuse_req_t req, fuse + if (arg->minor >= 6) { + if (arg->max_readahead < se->conn.max_readahead) + se->conn.max_readahead = arg->max_readahead; +- if (arg->flags & FUSE_ASYNC_READ) ++ inargflags = arg->flags; ++ if (inargflags & FUSE_INIT_EXT) ++ inargflags = inargflags | (uint64_t) arg->flags2 << 32; ++ if (inargflags & FUSE_ASYNC_READ) + se->conn.capable |= FUSE_CAP_ASYNC_READ; +- if (arg->flags & FUSE_POSIX_LOCKS) ++ if (inargflags & FUSE_POSIX_LOCKS) + se->conn.capable |= FUSE_CAP_POSIX_LOCKS; +- if (arg->flags & FUSE_ATOMIC_O_TRUNC) ++ if (inargflags & FUSE_ATOMIC_O_TRUNC) + se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; +- if (arg->flags & FUSE_EXPORT_SUPPORT) ++ if (inargflags & FUSE_EXPORT_SUPPORT) + se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; +- if (arg->flags & FUSE_DONT_MASK) ++ if (inargflags & FUSE_DONT_MASK) + se->conn.capable |= FUSE_CAP_DONT_MASK; +- if (arg->flags & FUSE_FLOCK_LOCKS) ++ if (inargflags & FUSE_FLOCK_LOCKS) + se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; +- if (arg->flags & FUSE_AUTO_INVAL_DATA) ++ if (inargflags & FUSE_AUTO_INVAL_DATA) + se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; +- if (arg->flags & FUSE_DO_READDIRPLUS) ++ if (inargflags & FUSE_DO_READDIRPLUS) + se->conn.capable |= FUSE_CAP_READDIRPLUS; +- if (arg->flags & FUSE_READDIRPLUS_AUTO) ++ if (inargflags & FUSE_READDIRPLUS_AUTO) + se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; +- if (arg->flags & FUSE_ASYNC_DIO) ++ if (inargflags & FUSE_ASYNC_DIO) + se->conn.capable |= FUSE_CAP_ASYNC_DIO; +- if (arg->flags & FUSE_WRITEBACK_CACHE) ++ if (inargflags & FUSE_WRITEBACK_CACHE) + se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; +- if (arg->flags & FUSE_NO_OPEN_SUPPORT) ++ if (inargflags & FUSE_NO_OPEN_SUPPORT) + se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; +- if (arg->flags & FUSE_PARALLEL_DIROPS) ++ if (inargflags & FUSE_PARALLEL_DIROPS) + se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; +- if (arg->flags & FUSE_POSIX_ACL) ++ if (inargflags & FUSE_POSIX_ACL) + se->conn.capable |= FUSE_CAP_POSIX_ACL; +- if (arg->flags & FUSE_HANDLE_KILLPRIV) ++ if (inargflags & FUSE_HANDLE_KILLPRIV) + se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; +- if (arg->flags & FUSE_CACHE_SYMLINKS) ++ if (inargflags & FUSE_CACHE_SYMLINKS) + se->conn.capable |= FUSE_CAP_CACHE_SYMLINKS; +- if (!(arg->flags & FUSE_MAX_PAGES)) { ++ if (!(inargflags & FUSE_MAX_PAGES)) { + size_t max_bufsize = + FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() + + FUSE_BUFFER_HEADER_SIZE; +@@ -1977,37 +1981,42 @@ static void do_init(fuse_req_t req, fuse + outarg.flags |= FUSE_MAX_PAGES; + outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1; + } +- ++ outargflags = outarg.flags; + /* Always enable big writes, this is superseded + by the max_write option */ +- outarg.flags |= FUSE_BIG_WRITES; ++ outargflags |= FUSE_BIG_WRITES; + + if (se->conn.want & FUSE_CAP_ASYNC_READ) +- outarg.flags |= FUSE_ASYNC_READ; ++ outargflags |= FUSE_ASYNC_READ; + if (se->conn.want & FUSE_CAP_POSIX_LOCKS) +- outarg.flags |= FUSE_POSIX_LOCKS; ++ outargflags |= FUSE_POSIX_LOCKS; + if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC) +- outarg.flags |= FUSE_ATOMIC_O_TRUNC; ++ outargflags |= FUSE_ATOMIC_O_TRUNC; + if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT) +- outarg.flags |= FUSE_EXPORT_SUPPORT; ++ outargflags |= FUSE_EXPORT_SUPPORT; + if (se->conn.want & FUSE_CAP_DONT_MASK) +- outarg.flags |= FUSE_DONT_MASK; ++ outargflags |= FUSE_DONT_MASK; + if (se->conn.want & FUSE_CAP_FLOCK_LOCKS) +- outarg.flags |= FUSE_FLOCK_LOCKS; ++ outargflags |= FUSE_FLOCK_LOCKS; + if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA) +- outarg.flags |= FUSE_AUTO_INVAL_DATA; ++ outargflags |= FUSE_AUTO_INVAL_DATA; + if (se->conn.want & FUSE_CAP_READDIRPLUS) +- outarg.flags |= FUSE_DO_READDIRPLUS; ++ outargflags |= FUSE_DO_READDIRPLUS; + if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO) +- outarg.flags |= FUSE_READDIRPLUS_AUTO; ++ outargflags |= FUSE_READDIRPLUS_AUTO; + if (se->conn.want & FUSE_CAP_ASYNC_DIO) +- outarg.flags |= FUSE_ASYNC_DIO; ++ outargflags |= FUSE_ASYNC_DIO; + if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE) +- outarg.flags |= FUSE_WRITEBACK_CACHE; ++ outargflags |= FUSE_WRITEBACK_CACHE; + if (se->conn.want & FUSE_CAP_POSIX_ACL) +- outarg.flags |= FUSE_POSIX_ACL; ++ outargflags |= FUSE_POSIX_ACL; + if (se->conn.want & FUSE_CAP_CACHE_SYMLINKS) +- outarg.flags |= FUSE_CACHE_SYMLINKS; ++ outargflags |= FUSE_CACHE_SYMLINKS; ++ ++ outarg.flags = outargflags; ++ ++ if (inargflags & FUSE_INIT_EXT) ++ outarg.flags2 = outargflags >> 32; + outarg.max_readahead = se->conn.max_readahead; + outarg.max_write = se->conn.max_write; + if (se->conn.proto_minor >= 13) { diff --git a/0006-BZ_2171095.patch b/0006-BZ_2171095.patch new file mode 100644 index 0000000..012f9e1 --- /dev/null +++ b/0006-BZ_2171095.patch @@ -0,0 +1,118 @@ +From 22cc0c761ad23395a8220ce1954cfb8a64f36ff4 Mon Sep 17 00:00:00 2001 +From: HereThereBeDragons +Date: Thu, 27 Oct 2022 17:52:10 +0200 +Subject: [PATCH] Initial patch provided by Miklos Szeredi + + +--- + include/fuse_kernel.h | 8 +++++++- + include/fuse_lowlevel.h | 8 ++++++++ + lib/fuse_lowlevel.c | 18 ++++++++++++++---- + lib/fuse_versionscript | 1 + + 4 files changed, 30 insertions(+), 5 deletions(-) + +diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h +index 09da620b5..e0666a1f7 100644 +--- a/include/fuse_kernel.h ++++ b/include/fuse_kernel.h +@@ -336,6 +336,12 @@ + */ + #define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0) + ++/** ++ * notify_inval_entry flags ++ * FUSE_EXPIRE_ONLY ++ */ ++#define FUSE_EXPIRE_ONLY (1 << 0) ++ + enum fuse_opcode { + FUSE_LOOKUP = 1, + FUSE_FORGET = 2, /* no reply */ +@@ -800,7 +806,7 @@ struct fuse_notify_inval_inode_out { + struct fuse_notify_inval_entry_out { + uint64_t parent; + uint32_t namelen; +- uint32_t padding; ++ uint32_t flags; + }; + + struct fuse_notify_delete_out { +diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h +index 53f0fcf4b..c955cc4bb 100644 +--- a/include/fuse_lowlevel.h ++++ b/include/fuse_lowlevel.h +@@ -1675,6 +1675,14 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, + int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, + const char *name, size_t namelen); + ++enum fuse_expire_flags { ++ FUSE_LL_EXPIRE_ONLY = (1 << 0), ++}; ++ ++int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, ++ const char *name, size_t namelen, ++ enum fuse_expire_flags flags); ++ + /** + * This function behaves like fuse_lowlevel_notify_inval_entry() with + * the following additional effect (at least as of Linux kernel 4.8): +diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c +index e82cd9e9f..7b9d71043 100644 +--- a/lib/fuse_lowlevel.c ++++ b/lib/fuse_lowlevel.c +@@ -2161,21 +2161,24 @@ + return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2); + } + +-int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, +- const char *name, size_t namelen) ++int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, ++ const char *name, size_t namelen, ++ enum fuse_expire_flags flags) + { + struct fuse_notify_inval_entry_out outarg; + struct iovec iov[3]; + + if (!se) + return -EINVAL; +- ++ + if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) + return -ENOSYS; + + outarg.parent = parent; + outarg.namelen = namelen; +- outarg.padding = 0; ++ outarg.flags = 0; ++ if (flags & FUSE_LL_EXPIRE_ONLY) ++ outarg.flags |= FUSE_EXPIRE_ONLY; + + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); +@@ -2292,6 +2295,13 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, + return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3); + } + ++int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, ++ const char *name, size_t namelen) ++{ ++ return fuse_lowlevel_notify_expire_entry(se, parent, name, namelen, 0); ++} ++ ++ + int fuse_lowlevel_notify_delete(struct fuse_session *se, + fuse_ino_t parent, fuse_ino_t child, + const char *name, size_t namelen) +diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript +index 7e50e7507..795ea4aa9 100644 +--- a/lib/fuse_versionscript ++++ b/lib/fuse_versionscript +@@ -151,6 +151,7 @@ + FUSE_3.3 { + global: + fuse_open_channel; ++ fuse_lowlevel_notify_expire_entr; + } FUSE_3.2; + + # Local Variables: + diff --git a/0007-BZ_2171095-cap.patch b/0007-BZ_2171095-cap.patch new file mode 100644 index 0000000..a42a567 --- /dev/null +++ b/0007-BZ_2171095-cap.patch @@ -0,0 +1,125 @@ +From 6b1612e3a85b993f82124cccf149df3830e4a9c5 Mon Sep 17 00:00:00 2001 +From: HereThereBeDragons +Date: Thu, 27 Oct 2022 17:52:10 +0200 +Subject: [PATCH] adding comments and capability discovery, enum for flags + moved to top of file + +--- + example/printcap.c | 2 ++ + include/fuse_common.h | 16 ++++++++++++++++ + include/fuse_lowlevel.h | 40 ++++++++++++++++++++++++++++++++++++---- + lib/fuse_lowlevel.c | 2 ++ + 4 files changed, 56 insertions(+), 4 deletions(-) + +diff --git a/example/printcap.c b/example/printcap.c +index edfd8f531..48679883c 100644 +--- a/example/printcap.c ++++ b/example/printcap.c +@@ -79,6 +79,8 @@ + printf("\tFUSE_CAP_POSIX_ACL\n"); + if(conn->capable & FUSE_CAP_CACHE_SYMLINKS) + printf("\tFUSE_CAP_CACHE_SYMLINKS\n"); ++ if(conn->capable & FUSE_CAP_EXPIRE_ONLY) ++ printf("\tFUSE_CAP_EXPIRE_ONLY\n"); + fuse_session_exit(se); + } + +diff --git a/include/fuse_common.h b/include/fuse_common.h +index e9d874556..dbba05af8 100644 +--- a/include/fuse_common.h ++++ b/include/fuse_common.h +@@ -338,6 +338,22 @@ + #define FUSE_CAP_CACHE_SYMLINKS (1 << 23) + + /** ++ * Indicates support that dentries can be expired or invalidated. ++ * ++ * Expiring dentries, instead of invalidating them, makes a difference for ++ * overmounted dentries, where plain invalidation would detach all submounts ++ * before dropping the dentry from the cache. If only expiry is set on the ++ * dentry, then any overmounts are left alone and until ->d_revalidate() ++ * is called. ++ * ++ * Note: ->d_revalidate() is not called for the case of following a submount, ++ * so invalidation will only be triggered for the non-overmounted case. ++ * The dentry could also be mounted in a different mount instance, in which case ++ * any submounts will still be detached. ++*/ ++#define FUSE_CAP_EXPIRE_ONLY (1 << 26) ++ ++/** + * Ioctl flags + * + * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine + +diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h +index c955cc4bb..6a1a5d534 100644 +--- a/include/fuse_lowlevel.h ++++ b/include/fuse_lowlevel.h +@@ -127,6 +127,15 @@ struct fuse_forget_data { + uint64_t nlookup; + }; + ++/** ++ * Flags for fuse_lowlevel_notify_expire_entry() ++ * 0 = invalidate entry ++ * FUSE_LL_EXPIRE_ONLY = expire entry ++*/ ++enum fuse_expire_flags { ++ FUSE_LL_EXPIRE_ONLY = (1 << 0), ++}; ++ + /* 'to_set' flags in setattr */ + #define FUSE_SET_ATTR_MODE (1 << 0) + #define FUSE_SET_ATTR_UID (1 << 1) +@@ -1675,10 +1684,33 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, + int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, + const char *name, size_t namelen); + +-enum fuse_expire_flags { +- FUSE_LL_EXPIRE_ONLY = (1 << 0), +-}; +- ++/** ++ * Notify to expire or invalidate parent attributes and the dentry ++ * matching parent/name ++ * ++ * Underlying function for fuse_lowlevel_notify_inval_entry(). ++ * ++ * In addition to invalidating an entry, it also allows to expire an entry. ++ * In that case, the entry is not forcefully removed from kernel cache ++ * but instead the next access to it forces a lookup from the filesystem. ++ * ++ * This makes a difference for overmounted dentries, where plain invalidation ++ * would detach all submounts before dropping the dentry from the cache. ++ * If only expiry is set on the dentry, then any overmounts are left alone and ++ * until ->d_revalidate() is called. ++ * ++ * Note: ->d_revalidate() is not called for the case of following a submount, ++ * so invalidation will only be triggered for the non-overmounted case. ++ * The dentry could also be mounted in a different mount instance, in which case ++ * any submounts will still be detached. ++ * ++ * @param se the session object ++ * @param parent inode number ++ * @param name file name ++ * @param namelen strlen() of file name ++ * @param flags flags to control if the entry should be expired or invalidated ++ * @return zero for success, -errno for failure ++*/ + int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, + const char *name, size_t namelen, + enum fuse_expire_flags flags); +diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c +index 7b9d71043..7d7630925 100644 +--- a/lib/fuse_lowlevel.c ++++ b/lib/fuse_lowlevel.c +@@ -1991,6 +1991,8 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + bufsize = max_bufsize; + } + } ++ if (arg->minor >= 38) ++ se->conn.capable |= FUSE_CAP_EXPIRE_ONLY; + } else { + se->conn.max_readahead = 0; + } diff --git a/0008-BZ_217095-libfuse-add-feature-flag-for-expire-only.patch b/0008-BZ_217095-libfuse-add-feature-flag-for-expire-only.patch new file mode 100644 index 0000000..89ac869 --- /dev/null +++ b/0008-BZ_217095-libfuse-add-feature-flag-for-expire-only.patch @@ -0,0 +1,45 @@ +From: Miklos Szeredi +Subject: libfuse: add feature flag for expire-only + +Add the FUSE_HAS_EXPIRE_ONLY flag. This should be used to set the +FUSE_CAP_EXPIRE_ONLY capability flag on the low level API instead of +checking for the version. + +Checking the version doesn't work if the feature is backported to an +earlier kernel. + +Signed-off-by: Miklos Szeredi +--- + include/fuse_kernel.h | 2 ++ + lib/fuse_lowlevel.c | 2 +- + 2 files changed, 3 insertions(+), 1 deletion(-) + +--- a/include/fuse_kernel.h ++++ b/include/fuse_kernel.h +@@ -248,6 +248,7 @@ + * FUSE_POSIX_ACL: filesystem supports posix acls + * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages + * FUSE_CACHE_SYMLINKS: cache READLINK responses ++ * FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation + */ + #define FUSE_ASYNC_READ (1 << 0) + #define FUSE_POSIX_LOCKS (1 << 1) +@@ -272,6 +273,7 @@ + #define FUSE_MAX_PAGES (1 << 22) + #define FUSE_CACHE_SYMLINKS (1 << 23) + #define FUSE_INIT_EXT (1 << 30) ++#define FUSE_HAS_EXPIRE_ONLY (1ULL << 35) + + /** + * CUSE INIT request/reply flags +--- a/lib/fuse_lowlevel.c ++++ b/lib/fuse_lowlevel.c +@@ -2008,7 +2008,7 @@ void do_init(fuse_req_t req, fuse_ino_t + bufsize = max_bufsize; + } + } +- if (arg->minor >= 38) ++ if (inargflags & FUSE_HAS_EXPIRE_ONLY) + se->conn.capable |= FUSE_CAP_EXPIRE_ONLY; + } else { + se->conn.max_readahead = 0; diff --git a/Jenkinsfile b/Jenkinsfile index fb579f1..c9499b8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -40,5 +40,6 @@ // I.e. for testing library changes //@Library(value="pipeline-lib@your_branch") _ +/* groovylint-disable-next-line CompileStatic */ packageBuildingPipelineDAOSTest(['distros' : ['el8'], - 'test-tag': 'dfuse,dfuseenospace']) + 'test-tag': 'dfuse']) diff --git a/fuse-3.17.0-Don-t-set-FUSE_CAP_PARALLEL_DIROPS-by-default.patch b/fuse-3.17.0-Don-t-set-FUSE_CAP_PARALLEL_DIROPS-by-default.patch new file mode 100644 index 0000000..f2a7e54 --- /dev/null +++ b/fuse-3.17.0-Don-t-set-FUSE_CAP_PARALLEL_DIROPS-by-default.patch @@ -0,0 +1,42 @@ +From 2c736f516f28dfb5c58aff345c668a5ea6386295 Mon Sep 17 00:00:00 2001 +From: Miklos Szeredi +Date: Wed, 10 Jan 2024 10:15:43 +0100 +Subject: [PATCH] Don't set FUSE_CAP_PARALLEL_DIROPS by default + +Allowing parallel dir operations could result in a crash in a filesystem +implementation that is not prepared for this. + +To be safe keep this flag off by default (this is not a regression, since +there was no public release where this flag wasn't ignored). + +If the filesystem wants better performance, then it should set this flag +explicitly. + +Fixes: c9905341ea34 ("Pass FUSE_PARALLEL_DIROPS to kernel (#861)") +Signed-off-by: Miklos Szeredi +--- + include/fuse_common.h | 2 -- + lib/fuse_lowlevel.c | 1 - + 2 files changed, 3 deletions(-) + +--- a/include/fuse_common.h ++++ b/include/fuse_common.h +@@ -313,8 +313,6 @@ struct fuse_loop_config { + * is unset, the FUSE kernel module will ensure that lookup() and + * readdir() requests are never issued concurrently for the same + * directory. +- * +- * This feature is enabled by default when supported by the kernel. + */ + #define FUSE_CAP_PARALLEL_DIROPS (1 << 18) + +--- a/lib/fuse_lowlevel.c ++++ b/lib/fuse_lowlevel.c +@@ -2009,7 +2009,6 @@ void do_init(fuse_req_t req, fuse_ino_t + if ((cond) && (se->conn.capable & (cap))) \ + se->conn.want |= (cap) + LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ); +- LL_SET_DEFAULT(1, FUSE_CAP_PARALLEL_DIROPS); + LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA); + LL_SET_DEFAULT(1, FUSE_CAP_HANDLE_KILLPRIV); + LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO); diff --git a/fuse-3.17.0-Pass-FUSE_PARALLEL_DIROPS-to-kernel-861.patch b/fuse-3.17.0-Pass-FUSE_PARALLEL_DIROPS-to-kernel-861.patch new file mode 100644 index 0000000..4e4420a --- /dev/null +++ b/fuse-3.17.0-Pass-FUSE_PARALLEL_DIROPS-to-kernel-861.patch @@ -0,0 +1,23 @@ +From c9905341ea34ff9acbc11b3c53ba8bcea35eeed8 Mon Sep 17 00:00:00 2001 +From: fdinoff +Date: Thu, 16 Nov 2023 06:23:20 -0500 +Subject: [PATCH] Pass FUSE_PARALLEL_DIROPS to kernel (#861) + +This tells the kernel that parallel lookup/readdir operations are +supported. This is enabled by default but was not passed to the kernel +so you always get the synchronized version. +--- + lib/fuse_lowlevel.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/lib/fuse_lowlevel.c ++++ b/lib/fuse_lowlevel.c +@@ -2093,6 +2093,8 @@ void do_init(fuse_req_t req, fuse_ino_t + outargflags |= FUSE_ASYNC_DIO; + if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE) + outargflags |= FUSE_WRITEBACK_CACHE; ++ if (se->conn.want & FUSE_CAP_PARALLEL_DIROPS) ++ outargflags |= FUSE_PARALLEL_DIROPS; + if (se->conn.want & FUSE_CAP_POSIX_ACL) + outargflags |= FUSE_POSIX_ACL; + if (se->conn.want & FUSE_CAP_CACHE_SYMLINKS) diff --git a/fuse.rpmlintrc b/fuse.rpmlintrc index a0399d5..c750697 100644 --- a/fuse.rpmlintrc +++ b/fuse.rpmlintrc @@ -1,8 +1,13 @@ +# This line is mandatory to access the configuration functions +from Config import * + # these are all from the upstream specfile -addFilter(r'fuse3?.x86_64: E: setuid-binary /usr/bin/fusermount3? root 4755') -addFilter('fuse.x86_64: W: package-with-huge-docs:') -addFilter(r'fuse3?.x86_64: E: non-standard-executable-perm /usr/bin/fusermount3? 4755') -addFilter(r'fuse3?.x86_64: W: no-manual-page-for-binary (fusermount3?|ulockmgr_server|mount.fuse3?)') -addFilter(r'fuse3?-devel.x86_64: W: no-documentation') -addFilter('fuse3.x86_64: E: missing-call-to-setgroups-before-setuid /usr/sbin/mount.fuse3') -addFilter('fuse-common.x86_64: W: description-shorter-than-summary') +addFilter(r'fuse3?\.x86_64: E: setuid-binary /usr/bin/fusermount3? root 4755') +addFilter('fuse\.x86_64: W: package-with-huge-docs:') +addFilter(r'fuse3?\.x86_64: E: non-standard-executable-perm /usr/bin/fusermount3? 4755') +addFilter(r'fuse3?\.x86_64: W: no-manual-page-for-binary (fusermount3?|ulockmgr_server|mount\.fuse3?)') +addFilter(r'fuse3?-devel\.x86_64: W: no-documentation') +addFilter(r'fuse3\.x86_64: E: missing-call-to-setgroups-before-setuid /usr/sbin/mount\.fuse3') +addFilter(r'fuse-common\.x86_64: W: description-shorter-than-summary') +addFilter(r'fuse\.x86_64: W: package-with-huge-docs') +addFilter(r'fuse-common\.x86_64: E: no-binary') diff --git a/fuse.spec b/fuse.spec index 7530a7f..62cde6d 100644 --- a/fuse.spec +++ b/fuse.spec @@ -4,7 +4,7 @@ Name: fuse Version: %{fuse2ver} -Release: 15.9%{?dist} +Release: 17.1%{?dist} Summary: File System in Userspace (FUSE) v2 utilities License: GPL+ URL: http://fuse.sf.net @@ -26,6 +26,12 @@ Patch8: 0001-Synchronize-fuse_kernel.h.patch Patch9: 0002-fuse_lowlevel-Add-max_pages-support-384.patch Patch10: 0003-Allow-caching-symlinks-in-kernel-page-cache.-551.patch Patch11: 0004-Add-support-for-in-kernel-readdir-caching.patch +Patch12: 0005-BZ_217095_Modify-structures-in-libfuse-to-handle-flags-beyond-rhel-8.patch +Patch13: 0006-BZ_2171095.patch +Patch14: 0007-BZ_2171095-cap.patch +Patch15: 0008-BZ_217095-libfuse-add-feature-flag-for-expire-only.patch +Patch16: fuse-3.17.0-Pass-FUSE_PARALLEL_DIROPS-to-kernel-861.patch +Patch17: fuse-3.17.0-Don-t-set-FUSE_CAP_PARALLEL_DIROPS-by-default.patch Requires: which Conflicts: filesystem < 3 @@ -109,7 +115,7 @@ License: GPL+ Common files for FUSE v2 and FUSE v3. %prep -%setup -q -T -c -n fuse2and3 -a0 -a1 +%setup -q -T -c -n fuse2and3 -a0 -a1 # fuse 3 pushd lib%{name}-%{name}-%{fuse3ver} @@ -120,6 +126,14 @@ pushd lib%{name}-%{name}-%{fuse3ver} %patch9 -p1 %patch10 -p1 %patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 + + popd @@ -251,7 +265,15 @@ rm -f %{buildroot}/usr/lib/udev/rules.d/99-fuse3.rules %{_includedir}/fuse3/ %changelog -* Mon May 30 2022 Pavel Reichl - 2.9.7-15.9 +* Fri Feb 02 2024 Pavel Reichl - 2.9.7-17.1 +- Advertise support of FUSE_PARALLEL_DIROPS to kernel +- Fixes RHEL-19149 + +* Thu Mar 23 2023 Pavel Reichl - 2.9.7-17 +- Add feature_notify_inode_expire_only +- Fixes rhbz#2171095 + +* Mon May 30 2022 Pavel Reichl - 2.9.7-16 - Back-port max_pages support, - caching symlinks in kernel page cache, - and in-kernel readdir caching diff --git a/packaging/Dockerfile.centos.7 b/packaging/Dockerfile.centos.7 index cdfb7f6..189ea1e 100644 --- a/packaging/Dockerfile.centos.7 +++ b/packaging/Dockerfile.centos.7 @@ -5,9 +5,31 @@ # # Pull base image -FROM centos:7 +FROM centos:centos7 LABEL maintainer="daos@daos.groups.io" +# Use local repo server if present +ARG REPO_FILE_URL +RUN set -e; \ + if [ -n "$REPO_FILE_URL" ]; then \ + cd /etc/yum.repos.d/ && \ + curl -k -f -o daos_ci-centos7-artifactory.repo.tmp \ + "$REPO_FILE_URL"daos_ci-centos7-artifactory.repo && \ + for file in *.repo; do \ + true > $file; \ + done; \ + mv daos_ci-centos7-artifactory.repo{.tmp,}; \ + fi; \ + yum -y install dnf; \ + yum clean all; \ + dnf --disablerepo \*epel\* -y install epel-release \ + dnf-plugins-core; \ + if [ -n "$REPO_FILE_URL" ]; then \ + dnf -y --quiet config-manager --disable epel; \ + fi; \ + dnf -y update epel-release; \ + dnf -y clean all + # use same UID as host and default value of 1000 if not specified ARG UID=1000 @@ -15,9 +37,9 @@ ARG UID=1000 #Nothing to do for CentOS # Install basic tools -RUN yum install -y epel-release -RUN yum install -y mock make rpm-build curl createrepo rpmlint redhat-lsb-core \ - git python-srpm-macros dnf +RUN dnf install -y epel-release +RUN dnf install -y mock make rpm-build curl createrepo rpmlint redhat-lsb-core \ + git python-srpm-macros dnf && dnf -y clean all # Add build user (to keep rpmbuild happy) ENV USER build diff --git a/packaging/Dockerfile.mockbuild b/packaging/Dockerfile.mockbuild index cb06c8d..76a6e94 100644 --- a/packaging/Dockerfile.mockbuild +++ b/packaging/Dockerfile.mockbuild @@ -1,27 +1,33 @@ # -# Copyright 2018-2022 Intel Corporation +# Copyright 2018-2024 Intel Corporation # # 'recipe' for Docker to build an RPM # # Pull base image -FROM fedora:latest +ARG FVERSION=latest +FROM fedora:$FVERSION +# Needed for later use of FVERSION +ARG FVERSION LABEL maintainer="daos@daos.groups.io" # Use local repo server if present ARG REPO_FILE_URL RUN if [ -n "$REPO_FILE_URL" ]; then \ cd /etc/yum.repos.d/ && \ - curl -f -o daos_ci-fedora-artifactory.repo.tmp \ + curl -k -f -o daos_ci-fedora-artifactory.repo.tmp \ "$REPO_FILE_URL"daos_ci-fedora-artifactory.repo && \ - rm -f *.repo && \ + for file in *.repo; do \ + true > $file; \ + done; \ mv daos_ci-fedora-artifactory.repo{.tmp,}; \ fi # Install basic tools RUN dnf -y install mock make \ rpm-build createrepo rpmlint redhat-lsb-core git \ - python-srpm-macros rpmdevtools + python-srpm-macros rpmdevtools && \ + dnf -y clean all # use same UID as host and default value of 1000 if not specified ARG UID=1000 @@ -40,11 +46,26 @@ RUN dnf -y upgrade && \ # Monkey-patch rpmlint until a new release is made with # https://github.com/rpm-software-management/rpmlint/pull/795 in it -COPY packaging/rpmlint--ignore-unused-rpmlintrc.patch . -RUN (cd $(python3 -c 'import site; print(site.getsitepackages()[-1])') && \ - patch -p1 && \ - rm -f rpmlint/__pycache__/{cli,lint}.*.pyc) < rpmlint--ignore-unused-rpmlintrc.patch && \ - rm -f rpmlint--ignore-unused-rpmlintrc.patch +# But make sure to patch after dnf upgrade so that an upgraded rpmlint +# RPM doesn't wipe out our patch +# Ditto for the patch to zero and display ccache stats +# https://github.com/rpm-software-management/mock/pull/1299 +ARG PACKAGINGDIR=packaging +COPY ${PACKAGINGDIR}/*.patch ./ +RUN (cd $(python3 -c 'import site; print(site.getsitepackages()[-1])') && \ + if ! grep -e --ignore-unused-rpmlintrc rpmlint/cli.py; then \ + if ! patch -p1 < $OLDPWD/rpmlint--ignore-unused-rpmlintrc.patch; then \ + exit 1; \ + fi; \ + rm -f rpmlint/__pycache__/{cli,lint}.*.pyc; \ + fi; \ + if ! grep _ccachePostBuildHook mockbuild/plugins/ccache.py; then \ + if ! patch -p3 < $OLDPWD/ccache-stats.patch; then \ + exit 1; \ + fi; \ + rm -f mockbuild/plugins/__pycache__/ccache.*.pyc; \ + fi); \ + rm -f rpmlint--ignore-unused-rpmlintrc.patch ccache-stats.patch # show the release that was built ARG CACHEBUST diff --git a/packaging/Dockerfile.ubuntu.20.04 b/packaging/Dockerfile.ubuntu.20.04 index f956145..ec76bfd 100644 --- a/packaging/Dockerfile.ubuntu.20.04 +++ b/packaging/Dockerfile.ubuntu.20.04 @@ -7,33 +7,60 @@ FROM ubuntu:20.04 LABEL org.opencontainers.image.authors="daos@daos.groups.io" -# use same UID as host and default value of 1000 if not specified -ARG UID=1000 -ARG REPO_URL="" -ARG REPO_UBUNTU_20_04="" +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + curl gpg + +ARG REPO_FILE_URL +RUN if [ -n "$REPO_FILE_URL" ]; then \ + cd /etc/apt/sources.list.d && \ + curl -f -o daos_ci-ubuntu20.04-artifactory.list.tmp \ + "$REPO_FILE_URL"daos_ci-ubuntu20.04-artifactory.list && \ + true > ../sources.list && \ + mv daos_ci-ubuntu20.04-artifactory.list.tmp \ + daos_ci-ubuntu20.04-artifactory.list; \ + fi; \ + cd -; \ + curl -f -O "$REPO_FILE_URL"esad_repo.key; \ + gpg --no-default-keyring --keyring ./temp-keyring.gpg \ + --import esad_repo.key; \ + mkdir -p /usr/local/share/keyrings/; \ + gpg --no-default-keyring --keyring ./temp-keyring.gpg --export \ + --output /usr/local/share/keyrings/daos-stack-public.gpg; \ + rm ./temp-keyring.gpg; \ + url_prefix=https://downloads.linux.hpe.com/SDR/; \ + for url in hpPublicKey2048.pub \ + hpPublicKey2048_key1.pub \ + hpePublicKey2048_key1.pub; do \ + curl -f -O "$url_prefix$url"; \ + gpg --no-default-keyring --keyring ./temp-keyring.gpg \ + --import "$(basename $url)"; \ + done; \ + gpg --no-default-keyring --keyring ./temp-keyring.gpg --export \ + --output /usr/local/share/keyrings/hpe-sdr-public.gpg; \ + rm ./temp-keyring.gpg # Install basic tools RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ autoconf bash ca-certificates curl debhelper dh-make \ - dpkg-dev dh-python doxygen gcc git git-buildpackage locales \ - make patch pbuilder pkg-config python3-dev python3-distro \ - python3-distutils rpm scons wget + dpkg-dev dh-python doxygen gcc git git-buildpackage \ + javahelper locales make patch pbuilder pkg-config \ + python3-dev python3-distro python3-distutils rpm scons wget \ + cmake valgrind rpmdevtools -# rpmdevtools -RUN echo "deb [trusted=yes] ${REPO_URL}${REPO_UBUNTU_20_04} focal main" > /etc/apt/sources.list.d/daos-stack-ubuntu-stable-local.list -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ - rpmdevtools +# use same UID as host and default value of 1000 if not specified +ARG UID=1000 # Add build user (to keep chrootbuild happy) ENV USER build RUN useradd -u $UID -ms /bin/bash $USER # need to run the build command as root, as it needs to chroot -RUN if ! grep "^#includedir /etc/sudoers.d" /etc/sudoers; then \ - echo "#includedir /etc/sudoers.d" >> /etc/sudoers; \ - fi; \ - echo "Defaults env_keep += \"DPKG_GENSYMBOLS_CHECK_LEVEL\"" > /etc/sudoers.d/build; \ - echo "build ALL=(ALL) NOPASSWD: /usr/sbin/pbuilder" >> /etc/sudoers.d/build; \ - chmod 0440 /etc/sudoers.d/build; \ - visudo -c; \ +RUN if ! grep "^#includedir /etc/sudoers.d" /etc/sudoers; then \ + echo "#includedir /etc/sudoers.d" >> /etc/sudoers; \ + fi; \ + echo "Defaults env_keep += \"DPKG_GENSYMBOLS_CHECK_LEVEL\"" > /etc/sudoers.d/build; \ + echo "build ALL=(ALL) NOPASSWD: /usr/bin/tee /root/.pbuilderrc" >> /etc/sudoers.d/build; \ + echo "build ALL=(ALL) NOPASSWD: /usr/sbin/pbuilder" >> /etc/sudoers.d/build; \ + chmod 0440 /etc/sudoers.d/build; \ + visudo -c; \ sudo -l -U build diff --git a/packaging/Makefile_distro_vars.mk b/packaging/Makefile_distro_vars.mk index 83ae764..4e8a09d 100644 --- a/packaging/Makefile_distro_vars.mk +++ b/packaging/Makefile_distro_vars.mk @@ -36,16 +36,28 @@ ORIG_TARGET_VER := 7 SED_EXPR := 1s/$(DIST)//p endif ifeq ($(patsubst %epel-8-x86_64,,$(lastword $(subst +, ,$(CHROOT_NAME)))),) -DIST := $(shell rpm $(COMMON_RPM_ARGS) --eval %{?dist}) -VERSION_ID := 8 -DISTRO_ID := el8 -DISTRO_BASE := EL_8 +DIST := $(shell rpm $(COMMON_RPM_ARGS) --eval %{?dist}) +VERSION_ID := 8 +DISTRO_ID := el8 +DISTRO_BASE := EL_8 ifneq ($(DISTRO_VERSION_EL8),) override DISTRO_VERSION := $(DISTRO_VERSION_EL8) endif -DISTRO_VERSION ?= $(VERSION_ID) -ORIG_TARGET_VER := 8.5 -SED_EXPR := 1s/$(DIST)//p +DISTRO_VERSION ?= $(VERSION_ID) +ORIG_TARGET_VER := 8 +SED_EXPR := 1s/$(DIST)//p +endif +ifeq ($(patsubst %epel-9-x86_64,,$(lastword $(subst +, ,$(CHROOT_NAME)))),) +DIST := $(shell rpm $(COMMON_RPM_ARGS) --eval %{?dist}) +VERSION_ID := 9 +DISTRO_ID := el9 +DISTRO_BASE := EL_9 +ifneq ($(DISTRO_VERSION_EL9),) +override DISTRO_VERSION := $(DISTRO_VERSION_EL9) +endif +DISTRO_VERSION ?= $(VERSION_ID) +ORIG_TARGET_VER := 9 +SED_EXPR := 1s/$(DIST)//p endif ifeq ($(CHROOT_NAME),opensuse-leap-15.2-x86_64) VERSION_ID := 15.2 @@ -60,7 +72,23 @@ VERSION_ID := 15.3 DISTRO_ID := sl15.3 DISTRO_BASE := LEAP_15 DISTRO_VERSION ?= $(VERSION_ID) -ORIG_TARGET_VER := 15.2 +ORIG_TARGET_VER := 15.3 +SED_EXPR := 1p +endif +ifeq ($(CHROOT_NAME),opensuse-leap-15.4-x86_64) +VERSION_ID := 15.4 +DISTRO_ID := sl15.4 +DISTRO_BASE := LEAP_15 +DISTRO_VERSION ?= $(VERSION_ID) +ORIG_TARGET_VER := 15.4 +SED_EXPR := 1p +endif +ifeq ($(CHROOT_NAME),opensuse-leap-15.5-x86_64) +VERSION_ID := 15.5 +DISTRO_ID := sl15.5 +DISTRO_BASE := LEAP_15 +DISTRO_VERSION ?= $(VERSION_ID) +ORIG_TARGET_VER := 15.5 SED_EXPR := 1p endif endif diff --git a/packaging/Makefile_packaging.mk b/packaging/Makefile_packaging.mk index ce777eb..3201a22 100644 --- a/packaging/Makefile_packaging.mk +++ b/packaging/Makefile_packaging.mk @@ -6,7 +6,7 @@ # force bash (looking at you Ubuntu) SHELL=/bin/bash -# Put site overrides (i.e. REPOSITORY_URL, DAOS_STACK_*_LOCAL_REPO) in here +# Put site overrides (i.e. DAOS_STACK_*_LOCAL_REPO) in here -include Makefile.local # default to Leap 15 distro for chrootbuild @@ -39,6 +39,7 @@ PR_REPOS ?= $(shell git show -s --format=%B | sed -ne 's/^PR-rep LEAP_15_PR_REPOS ?= $(shell git show -s --format=%B | sed -ne 's/^PR-repos-leap15: *\(.*\)/\1/p') EL_7_PR_REPOS ?= $(shell git show -s --format=%B | sed -ne 's/^PR-repos-el7: *\(.*\)/\1/p') EL_8_PR_REPOS ?= $(shell git show -s --format=%B | sed -ne 's/^PR-repos-el8: *\(.*\)/\1/p') +EL_9_PR_REPOS ?= $(shell git show -s --format=%B | sed -ne 's/^PR-repos-el9: *\(.*\)/\1/p') UBUNTU_20_04_PR_REPOS ?= $(shell git show -s --format=%B | sed -ne 's/^PR-repos-ubuntu20: *\(.*\)/\1/p') REPO_FILES_PR ?= $(shell git show -s --format=%B | sed -ne 's/^Repo-files-PR: *\(.*\)/\1/p') @@ -53,6 +54,7 @@ RPM_BUILD_OPTIONS := $(BUILD_DEFINES) GIT_DIFF_EXCLUDES := $(PATCH_EXCLUDE_FILES:%=':!%') endif +FVERSION ?= latest COMMON_RPM_ARGS := --define "_topdir $$PWD/_topdir" $(BUILD_DEFINES) SPEC := $(shell if [ -f $(NAME)-$(DISTRO_BASE).spec ]; then echo $(NAME)-$(DISTRO_BASE).spec; else echo $(NAME).spec; fi) VERSION = $(eval VERSION := $(shell rpm $(COMMON_RPM_ARGS) --specfile --qf '%{version}\n' $(SPEC) | sed -n '1p'))$(VERSION) @@ -64,10 +66,16 @@ RPMS = $(eval RPMS := $(addsuffix .rpm,$(addprefix _topdir/RPMS/x86 DEB_TOP := _topdir/BUILD DEB_BUILD := $(DEB_TOP)/$(NAME)-$(VERSION) DEB_TARBASE := $(DEB_TOP)/$(DEB_NAME)_$(VERSION) -SOURCE ?= $(eval SOURCE := $(shell CHROOT_NAME=$(CHROOT_NAME) $(SPECTOOL) $(COMMON_RPM_ARGS) -S -l $(SPEC) | sed -e 2,\$$d -e 's/\#/\\\#/g' -e 's/.*: *//'))$(SOURCE) -PATCHES ?= $(eval PATCHES := $(shell CHROOT_NAME=$(CHROOT_NAME) $(SPECTOOL) $(COMMON_RPM_ARGS) -l $(SPEC) | sed -ne 1d -e 's/.*: *//' -e 's/.*\///' -e '/\.patch/p'))$(PATCHES) -OTHER_SOURCES := $(eval OTHER_SOURCES := $(shell CHROOT_NAME=$(CHROOT_NAME) $(SPECTOOL) $(COMMON_RPM_ARGS) -l $(SPEC) | sed -ne 1d -e 's/.*: *//' -e 's/.*\///' -e '/\.patch/d' -e p))$(OTHER_SOURCES) -SOURCES := $(addprefix _topdir/SOURCES/,$(notdir $(SOURCE)) $(PATCHES) $(OTHER_SOURCES)) +REAL_SOURCE ?= $(eval REAL_SOURCE := $(shell CHROOT_NAME=$(CHROOT_NAME) $(SPECTOOL) $(COMMON_RPM_ARGS) -S -l $(SPEC) | sed -e 2,\$$d -e 's/\#/\\\#/g' -e 's/Source.*: *//'))$(REAL_SOURCE) +ifeq ($(ID_LIKE),debian) +ifneq ($(DEB_SOURCE),) +SOURCE ?= $(DEB_SOURCE) +endif +endif +SOURCE ?= $(REAL_SOURCE) +PATCHES ?= $(eval PATCHES := $(shell CHROOT_NAME=$(CHROOT_NAME) $(SPECTOOL) $(COMMON_RPM_ARGS) -l $(SPEC) | sed -ne 1d -e '/already present/d' -e 's/.*: *//' -e 's/.*\///' -e '/\.patch/p'))$(PATCHES) +OTHER_SOURCES := $(eval OTHER_SOURCES := $(shell CHROOT_NAME=$(CHROOT_NAME) $(SPECTOOL) $(COMMON_RPM_ARGS) -l $(SPEC) | sed -ne 1d -e '/already present/d' -e '/^Patch.*:/d' -e 's/Source.*: *//' -e 's/.*\///' -e p))$(OTHER_SOURCES) +SOURCES := $(addprefix _topdir/SOURCES/,$(notdir $(SOURCE) $(OTHER_SOURCES)) $(PATCHES)) ifeq ($(ID_LIKE),debian) DEBS := $(addsuffix _$(VERSION)-1_amd64.deb,$(shell sed -n '/-udeb/b; s,^Package:[[:blank:]],$(DEB_TOP)/,p' $(TOPDIR)/debian/control)) DEB_PREV_RELEASE := $(shell cd $(TOPDIR) && dpkg-parsechangelog -S version) @@ -84,7 +92,7 @@ define distro_map case $(DISTRO_ID) in \ el7) distro="centos7" \ ;; \ - el8) distro="el8" \ + el*) distro="$(DISTRO_ID)" \ ;; \ sle12.3) distro="sles12.3" \ ;; \ @@ -154,9 +162,9 @@ ifeq ($(DL_NAME),) DL_NAME = $(NAME) endif -$(notdir $(SOURCE)): $(SPEC) $(CALLING_MAKEFILE) +$(notdir $(SOURCE) $(OTHER_SOURCES) $(REAL_SOURCE)): $(SPEC) $(CALLING_MAKEFILE) # TODO: need to clean up old ones - $(SPECTOOL) -g $(SPEC) + $(SPECTOOL) $(COMMON_RPM_ARGS) -g $(SPEC) $(DEB_TOP)/%: % | $(DEB_TOP)/ @@ -181,27 +189,29 @@ $(DEB_TOP)/.patched: $(PATCHES) check-env deb_detar | \ $(DEB_BUILD)/debian/ mkdir -p ${DEB_BUILD}/debian/patches mkdir -p $(DEB_TOP)/patches - for f in $(PATCHES); do \ - rm -f $(DEB_TOP)/patches/*; \ - if git mailsplit -o$(DEB_TOP)/patches < "$$f" ;then \ - fn=$$(basename "$$f"); \ - for f1 in $(DEB_TOP)/patches/*;do \ - [ -e "$$f1" ] || continue; \ - f1n=$$(basename "$$f1"); \ - echo "$${fn}_$${f1n}" >> $(DEB_BUILD)/debian/patches/series ; \ - mv "$$f1" $(DEB_BUILD)/debian/patches/$${fn}_$${f1n}; \ - done; \ - else \ - fb=$$(basename "$$f"); \ - cp "$$f" $(DEB_BUILD)/debian/patches/ ; \ - echo "$$fb" >> $(DEB_BUILD)/debian/patches/series ; \ - if ! grep -q "^Description:\|^Subject:" "$$f" ;then \ - sed -i '1 iSubject: Auto added patch' \ - "$(DEB_BUILD)/debian/patches/$$fb" ;fi ; \ - if ! grep -q "^Origin:\|^Author:\|^From:" "$$f" ;then \ - sed -i '1 iOrigin: other' \ - "$(DEB_BUILD)/debian/patches/$$fb" ;fi ; \ - fi ; \ + for f in $(PATCHES); do \ + rm -f $(DEB_TOP)/patches/*; \ + if git mailsplit -o$(DEB_TOP)/patches < "$$f"; then \ + fn=$$(basename "$$f"); \ + for f1 in $(DEB_TOP)/patches/*;do \ + [ -e "$$f1" ] || continue; \ + f1n=$$(basename "$$f1"); \ + echo "$${fn}_$${f1n}" >> $(DEB_BUILD)/debian/patches/series; \ + mv "$$f1" $(DEB_BUILD)/debian/patches/$${fn}_$${f1n}; \ + done; \ + else \ + fb=$$(basename "$$f"); \ + cp "$$f" $(DEB_BUILD)/debian/patches/; \ + echo "$$fb" >> $(DEB_BUILD)/debian/patches/series; \ + if ! grep -q "^Description:\|^Subject:" "$$f"; then \ + sed -i '1 iSubject: Auto added patch' \ + "$(DEB_BUILD)/debian/patches/$$fb"; \ + fi; \ + if ! grep -q "^Origin:\|^Author:\|^From:" "$$f"; then \ + sed -i '1 iOrigin: other' \ + "$(DEB_BUILD)/debian/patches/$$fb"; \ + fi; \ + fi; \ done touch $@ @@ -271,6 +281,9 @@ $(DEB_TOP)/$(DEB_DSC): $(CALLING_MAKEFILE) $(DEB_BUILD).tar.$(SRC_EXT) \ cd $(DEB_BUILD); dpkg-buildpackage -S --no-sign --no-check-builddeps $(SRPM): $(SPEC) $(SOURCES) + if [ -f bz-1955184_find-requires ]; then \ + chmod 755 bz-1955184_find-requires; \ + fi rpmbuild -bs $(COMMON_RPM_ARGS) $(RPM_BUILD_OPTIONS) $(SPEC) srpm: $(SRPM) @@ -317,20 +330,6 @@ patch: echo "PKG_GIT_COMMIT is not defined" endif -# *_LOCAL_* repos are locally built packages. -ifeq ($(LOCAL_REPOS),true) - ifneq ($(ARTIFACTORY_URL),) - ifneq ($(DAOS_STACK_$(DISTRO_BASE)_LOCAL_REPO),) - DISTRO_REPOS = disabled # any non-empty value here works and is not used beyond testing if the value is empty or not - # convert to artifactory url - DAOS_STACK_$(DISTRO_BASE)_LOCAL_REPO := $(subst reposi,artifac,$(DAOS_STACK_$(DISTRO_BASE)_LOCAL_REPO)) - # $(DISTRO_BASE)_LOCAL_REPOS is a list separated by | because you cannot pass lists - # of values with spaces as environment variables - $(DISTRO_BASE)_LOCAL_REPOS := [trusted=yes] $(ARTIFACTORY_URL)$(subst stack,stack-daos,$(DAOS_STACK_$(DISTRO_BASE)_LOCAL_REPO)) - $(DISTRO_BASE)_LOCAL_REPOS += |[trusted=yes] $(ARTIFACTORY_URL)$(subst stack,stack-deps,$(DAOS_STACK_$(DISTRO_BASE)_LOCAL_REPO)) - endif #ifneq ($(DAOS_STACK_$(DISTRO_BASE)_LOCAL_REPO),) - endif # ifneq ($(ARTIFACTORY_URL),) -endif # ifeq ($(LOCAL_REPOS),true) ifeq ($(ID_LIKE),debian) chrootbuild: $(DEB_TOP)/$(DEB_DSC) $(call distro_map) \ @@ -346,6 +345,8 @@ chrootbuild: $(DEB_TOP)/$(DEB_DSC) DEB_TOP="$(DEB_TOP)" \ DEB_DSC="$(DEB_DSC)" \ DISTRO_ID_OPT="$(DISTRO_ID_OPT)" \ + LOCAL_REPOS='$(LOCAL_REPOS)' \ + ARTIFACTORY_URL="$(ARTIFACTORY_URL)" \ packaging/debian_chrootbuild else chrootbuild: $(SRPM) $(CALLING_MAKEFILE) @@ -360,16 +361,17 @@ chrootbuild: $(SRPM) $(CALLING_MAKEFILE) REPO_FILE_URL="$(REPO_FILE_URL)" \ MOCK_OPTIONS="$(MOCK_OPTIONS)" \ RPM_BUILD_OPTIONS='$(RPM_BUILD_OPTIONS)' \ - DISTRO_REPOS='$(DISTRO_REPOS)' \ + LOCAL_REPOS='$(LOCAL_REPOS)' \ ARTIFACTORY_URL="$(ARTIFACTORY_URL)" \ - REPOSITORY_URL="$(REPOSITORY_URL)" \ DISTRO_VERSION="$(DISTRO_VERSION)" \ + PACKAGE="$(NAME)" \ TARGET="$<" \ packaging/rpm_chrootbuild endif podman_chrootbuild: if ! podman build --build-arg REPO_FILE_URL=$(REPO_FILE_URL) \ + --build-arg FVERSION=$(FVERSION) \ -t $(subst +,-,$(CHROOT_NAME))-chrootbuild \ -f packaging/Dockerfile.mockbuild .; then \ echo "Container build failed"; \ @@ -380,7 +382,6 @@ podman_chrootbuild: -it $(subst +,-,$(CHROOT_NAME))-chrootbuild \ bash -c 'if ! DISTRO_REPOS=false \ REPO_FILE_URL=$(REPO_FILE_URL) \ - REPOSITORY_URL=$(REPOSITORY_URL) \ make REPO_FILES_PR=$(REPO_FILES_PR) \ MOCK_OPTIONS=$(MOCK_OPTIONS) \ CHROOT_NAME=$(CHROOT_NAME) -C $(CURDIR) chrootbuild; then \ @@ -388,7 +389,9 @@ podman_chrootbuild: exit 1; \ fi; \ rpmlint $$(ls /var/lib/mock/$(CHROOT_NAME)/result/*.rpm | \ - grep -v -e debuginfo -e debugsource -e src.rpm)' + grep -v -e debuginfo -e debugsource -e src.rpm)'; then \ + exit 1; \ + fi docker_chrootbuild: if ! $(DOCKER) build --build-arg UID=$$(id -u) -t chrootbuild \ @@ -406,9 +409,6 @@ docker_chrootbuild: exit 1; \ fi -mockbuild: $(SRPM) - mock -r $(CHROOT_NAME) $(MOCK_OPTIONS) $< - rpmlint: $(SPEC) rpmlint --ignore-unused-rpmlintrc $< @@ -430,6 +430,7 @@ packaging_check: --exclude install \ --exclude packaging \ --exclude utils \ + --exclude .vscode \ -bur $(PACKAGING_CHECK_DIR)/ packaging/; then \ exit 1; \ fi @@ -480,6 +481,9 @@ show_rpms: show_source: @echo '$(SOURCE)' +show_real_source: + @echo '$(REAL_SOURCE)' + show_patches: @echo '$(PATCHES)' diff --git a/packaging/ccache-stats.patch b/packaging/ccache-stats.patch new file mode 100644 index 0000000..26d5eeb --- /dev/null +++ b/packaging/ccache-stats.patch @@ -0,0 +1,66 @@ +From e87d916d7f49ea4949973adf0f09e9e5bf891e03 Mon Sep 17 00:00:00 2001 +From: "Brian J. Murrell" +Date: Tue, 30 Jan 2024 11:03:12 -0500 +Subject: [PATCH 1/2] Show ccache stats at the end of the build + +Zero the ccache stats at the beginning of the build and then display the +ccache stats at the end of the build to see how effective ccache was. + +Signed-off-by: Brian J. Murrell +--- + mock/py/mockbuild/plugins/ccache.py | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/mock/py/mockbuild/plugins/ccache.py b/mock/py/mockbuild/plugins/ccache.py +index 2666ad9fc..1080ffe68 100644 +--- a/mock/py/mockbuild/plugins/ccache.py ++++ b/mock/py/mockbuild/plugins/ccache.py +@@ -35,6 +35,7 @@ def __init__(self, plugins, conf, buildroot): + buildroot.preexisting_deps.append("ccache") + plugins.add_hook("prebuild", self._ccacheBuildHook) + plugins.add_hook("preinit", self._ccachePreInitHook) ++ plugins.add_hook("postbuild", self._ccachePostBuildHook) + buildroot.mounts.add( + BindMountPoint(srcpath=self.ccachePath, bindpath=buildroot.make_chroot_path("/var/tmp/ccache"))) + +@@ -47,6 +48,9 @@ def __init__(self, plugins, conf, buildroot): + @traceLog() + def _ccacheBuildHook(self): + self.buildroot.doChroot(["ccache", "-M", str(self.ccache_opts['max_cache_size'])], shell=False) ++ # zero ccache stats ++ getLog().info("Zero ccache stats:") ++ self.buildroot.doChroot(["ccache", "--zero-stats"], printOutput=True, shell=False) + + # set up the ccache dir. + # we also set a few variables used by ccache to find the shared cache. +@@ -61,3 +65,10 @@ def _ccachePreInitHook(self): + file_util.mkdirIfAbsent(self.buildroot.make_chroot_path('/var/tmp/ccache')) + file_util.mkdirIfAbsent(self.ccachePath) + self.buildroot.uid_manager.changeOwner(self.ccachePath, recursive=True) ++ ++ # get some cache stats ++ def _ccachePostBuildHook(self): ++ # show the cache hit stats ++ getLog().info("ccache stats:") ++ self.buildroot.doChroot(["ccache", "--show-stats"], printOutput=True, shell=False) +++ + +From bfd3a7e1bb47d28ee60a94cb5985c1f66476475f Mon Sep 17 00:00:00 2001 +From: "Brian J. Murrell" +Date: Tue, 30 Jan 2024 11:17:48 -0500 +Subject: [PATCH 2/2] Remove extraneous line + +Signed-off-by: Brian J. Murrell +--- + mock/py/mockbuild/plugins/ccache.py | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/mock/py/mockbuild/plugins/ccache.py b/mock/py/mockbuild/plugins/ccache.py +index 1080ffe68..1a20846d3 100644 +--- a/mock/py/mockbuild/plugins/ccache.py ++++ b/mock/py/mockbuild/plugins/ccache.py +@@ -71,4 +71,3 @@ def _ccachePostBuildHook(self): + # show the cache hit stats + getLog().info("ccache stats:") + self.buildroot.doChroot(["ccache", "--show-stats"], printOutput=True, shell=False) +-+ diff --git a/packaging/debian_chrootbuild b/packaging/debian_chrootbuild index 5ea4ead..cc2cc96 100755 --- a/packaging/debian_chrootbuild +++ b/packaging/debian_chrootbuild @@ -2,17 +2,17 @@ set -uex -# shellcheck disable=SC2153 -IFS=\| read -r -a distro_base_local_repos <<< "$DISTRO_BASE_LOCAL_REPOS" +if [ -n "${ARTIFACTORY_URL:-}" ] && "$LOCAL_REPOS"; then + echo "MIRRORSITE=${ARTIFACTORY_URL}artifactory/ubuntu-proxy" | sudo tee /root/.pbuilderrc +fi # shellcheck disable=SC2086 -sudo pbuilder create \ - --extrapackages "gnupg ca-certificates" \ - --othermirror \ - "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ $VERSION_CODENAME universe|deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ $VERSION_CODENAME-updates main universe" \ +sudo pbuilder create \ + --extrapackages "gnupg ca-certificates" \ $DISTRO_ID_OPT repo_args="" +repos_added=() for repo in $DISTRO_BASE_PR_REPOS $PR_REPOS; do branch="master" build_number="lastSuccessfulBuild" @@ -24,11 +24,32 @@ for repo in $DISTRO_BASE_PR_REPOS $PR_REPOS; do branch="${branch%:*}" fi fi + if [[ " ${repos_added[*]} " = *\ ${repo}\ * ]]; then + # don't add duplicates, first found wins + continue + fi + repos_added+=("$repo") repo_args="$repo_args|deb [trusted=yes] ${JENKINS_URL:-https://build.hpdd.intel.com/}job/daos-stack/job/$repo/job/$branch/$build_number/artifact/artifacts/$DISTRO/ ./" done -for repo in $JOB_REPOS "${distro_base_local_repos[@]}"; do - repo_args="$repo_args|deb ${repo} $VERSION_CODENAME main" + +repo_args+="|$(curl -sSf "$REPO_FILE_URL"daos_ci-"$DISTRO"-artifactory.list | + sed -e 's/#.*//' -e '/ubuntu-proxy/d' -e '/^$/d' -e '/^$/d' \ + -e 's/signed-by=.*\.gpg/trusted=yes/' | + sed -e ':a; N; $!ba; s/\n/|/g')" +for repo in $JOB_REPOS; do + repo_name=${repo##*://} + repo_name=${repo_name//\//_} + if [[ " ${repos_added[*]} " = *\ ${repo_name}\ * ]]; then + # don't add duplicates, first found wins + continue + fi + repos_added+=("$repo_name") + repo_args+="|deb ${repo} $VERSION_CODENAME main" done +# NB: This PPA is needed to support modern go toolchains on ubuntu 20.04. +# After the build is updated to use 22.04, which supports go >= 1.18, it +# should no longer be needed. +repo_args="$repo_args|deb [trusted=yes] https://ppa.launchpadcontent.net/longsleep/golang-backports/ubuntu $VERSION_CODENAME main" echo "$repo_args" if [ "$repo_args" = "|" ]; then repo_args="" diff --git a/packaging/get_base_branch b/packaging/get_base_branch new file mode 100755 index 0000000..27515a7 --- /dev/null +++ b/packaging/get_base_branch @@ -0,0 +1,22 @@ +#!/bin/bash + +# find the base branch of the current branch + +set -eux -o pipefail +IFS=' ' read -r -a add_bases <<< "${1:-}" +origin=origin +mapfile -t all_bases < <(echo "master" + git branch -r | sed -ne "/^ $origin\\/release\\/[0-9]/s/^ $origin\\///p") +all_bases+=("${add_bases[@]}") +TARGET="master" +min_diff=-1 +for base in "${all_bases[@]}"; do + git rev-parse --verify "$origin/$base" &> /dev/null || continue + commits_ahead=$(git log --oneline "$origin/$base..HEAD" | wc -l) + if [ "$min_diff" -eq -1 ] || [ "$min_diff" -gt "$commits_ahead" ]; then + TARGET="$base" + min_diff=$commits_ahead + fi +done +echo "$TARGET" +exit 0 diff --git a/packaging/rpm_chrootbuild b/packaging/rpm_chrootbuild index 03ab4c5..4dcdaa4 100755 --- a/packaging/rpm_chrootbuild +++ b/packaging/rpm_chrootbuild @@ -2,33 +2,44 @@ set -uex -mock_config_dir="${WORKSPACE:-$PWD}/mock" -original_cfg_file="/etc/mock/${CHROOT_NAME}.cfg" -cfg_file="$mock_config_dir/${CHROOT_NAME}.cfg" -mkdir -p "$mock_config_dir" -ln -sf /etc/mock/templates "$mock_config_dir/" -ln -sf /etc/mock/logging.ini "$mock_config_dir/" +cp /etc/mock/"$CHROOT_NAME".cfg mock.cfg + +# Enable mock ccache plugin +cat <> mock.cfg +config_opts['plugin_conf']['ccache_enable'] = True +config_opts['plugin_conf']['ccache_opts']['dir'] = "%(cache_topdir)s/%(root)s/ccache/" +EOF -cp "$original_cfg_file" "$cfg_file" if [[ $CHROOT_NAME == *epel-8-x86_64 ]]; then - cat <> "$cfg_file" + cat <> mock.cfg config_opts['module_setup_commands'] = [ ('enable', 'javapackages-tools:201801'), ('disable', 'go-toolset') ] EOF +fi +# Use dnf on CentOS 7 +if [[ $CHROOT_NAME == *epel-7-x86_64 ]]; then + MOCK_OPTIONS="--dnf --no-bootstrap-chroot${MOCK_OPTIONS:+ }$MOCK_OPTIONS" fi +# Allow BR: foo-devel < 1.2 to work when foo-devel-1.3 is actually available +cat <> mock.cfg +config_opts['dnf.conf'] += """ +[main] +best=0 +""" +EOF + # shellcheck disable=SC2153 repo_adds=() repo_dels=() -echo -e "config_opts['yum.conf'] += \"\"\"\n" >> "$cfg_file" +echo -e "config_opts['yum.conf'] += \"\"\"\n" >> mock.cfg -if { [ -n "${REPOSITORY_URL:-}" ] || [ -n "${ARTIFACTORY_URL:-}" ]; } \ - && [ -n "$DISTRO_REPOS" ]; then +if [ -n "${ARTIFACTORY_URL:-}" ] && "$LOCAL_REPOS"; then repo_dels+=("--disablerepo=\*") if [ -n "${REPO_FILE_URL:-}" ]; then @@ -49,11 +60,12 @@ if { [ -n "${REPOSITORY_URL:-}" ] || [ -n "${ARTIFACTORY_URL:-}" ]; } \ REPO_FILE_URL="file://$(readlink -e "$REPO_FILES_PR")/" fi fi - curl -sSf "$REPO_FILE_URL"daos_ci-"$DISTRO"-mock-artifactory.repo >> "$cfg_file" + curl -sSf "$REPO_FILE_URL"daos_ci-"${CHROOT_NAME%-*}".repo >> mock.cfg repo_adds+=("--enablerepo *-artifactory") fi fi +repos_added=() for repo in $DISTRO_BASE_PR_REPOS $PR_REPOS; do branch="master" build_number="lastSuccessfulBuild" @@ -65,28 +77,64 @@ for repo in $DISTRO_BASE_PR_REPOS $PR_REPOS; do branch="${branch%:*}" fi fi - repo_adds+=("--enablerepo $repo:$branch:$build_number") - echo -e "[$repo:$branch:$build_number]\n\ -name=$repo:$branch:$build_number\n\ -baseurl=${JENKINS_URL:-https://build.hpdd.intel.com/}job/daos-stack/job/$repo/job/$branch/$build_number/artifact/artifacts/$DISTRO/\n\ + if [[ " ${repos_added[*]} " = *\ ${repo}\ * ]]; then + # don't add duplicates, first found wins + continue + fi + repos_added+=("$repo") + repo_adds+=("--enablerepo $repo:${branch//[@\/]/_}:$build_number") + echo -e "[$repo:${branch//[@\/]/_}:$build_number]\n\ +name=$repo:${branch//[@\/]/_}:$build_number\n\ +baseurl=${ARTIFACTS_URL:-${JENKINS_URL:-https://build.hpdd.intel.com/}job/}daos-stack/job/$repo/job/${branch//\//%2F}/$build_number/artifact/artifacts/$DISTRO/\n\ enabled=1\n\ -gpgcheck=False\n" >> "$cfg_file" +gpgcheck=False\n" >> mock.cfg done for repo in $JOB_REPOS; do repo_name=${repo##*://} repo_name=${repo_name//\//_} + if [[ " ${repos_added[*]} " = *\ ${repo_name}\ * ]]; then + # don't add duplicates, first found wins + continue + fi + repos_added+=("$repo_name") repo_adds+=("--enablerepo $repo_name") - echo -e "[${repo_name//@/_}]\n\ + echo -e "[${repo_name//[@\/]/_}]\n\ name=${repo_name}\n\ -baseurl=${repo}\n\ -enabled=1\n" >> "$cfg_file" +baseurl=${repo//\//%2F}\n\ +enabled=1\n" >> mock.cfg done -echo "\"\"\"" >> "$cfg_file" +echo "\"\"\"" >> mock.cfg if [ -n "$DISTRO_VERSION" ]; then releasever_opt=("--config-opts=releasever=$DISTRO_VERSION") fi -# shellcheck disable=SC2086 -eval mock --configdir "$mock_config_dir" -r "${CHROOT_NAME}" \ - ${repo_dels[*]} ${repo_adds[*]} --disablerepo=\*-debug* \ - "${releasever_opt[@]}" $MOCK_OPTIONS $RPM_BUILD_OPTIONS "$TARGET" + +bs_dir=/scratch/mock/cache/"${CHROOT_NAME}"-bootstrap +if ls -l "$bs_dir"/root_cache/cache.tar.gz; then + mkdir -p "/var/cache/mock/${CHROOT_NAME}-bootstrap/" + flock "$bs_dir" -c "cp -a $bs_dir/root_cache /var/cache/mock/${CHROOT_NAME}-bootstrap" +fi +if ls -l "$bs_dir/ccache-$CHROOT_NAME-$PACKAGE".tar.gz; then + flock "$bs_dir" -c "tar -C / -xzf $bs_dir/ccache-$CHROOT_NAME-$PACKAGE.tar.gz" +fi + +rc=0 +# shellcheck disable=SC2086,SC2048 +if ! eval time mock -r mock.cfg ${repo_dels[*]} ${repo_adds[*]} --no-clean \ + --disablerepo=\*-debug* ${releasever_opt[*]} $MOCK_OPTIONS \ + $RPM_BUILD_OPTIONS "$TARGET"; then + rc=${PIPESTATUS[0]} +fi + +# Save the ccache +if [ -d /scratch/ ]; then + mkdir -p "$bs_dir"/ + flock "$bs_dir" -c "tar -czf $bs_dir/ccache-$CHROOT_NAME-$PACKAGE.tar.gz /var/cache/mock/${CHROOT_NAME}/ccache" + if ls -l /var/cache/mock/"${CHROOT_NAME}"-bootstrap/root_cache/cache.tar.gz; then + if ! cmp /var/cache/mock/"${CHROOT_NAME}"-bootstrap/root_cache/cache.tar.gz "$bs_dir"/root_cache/cache.tar.gz; then + flock "$bs_dir" -c "cp -a /var/cache/mock/${CHROOT_NAME}-bootstrap/root_cache $bs_dir/" + fi + fi +fi + +exit "$rc"