+ RETURN_ERROR_IF_RO_OR_LO;
+- return rename(fname1, fname2);
++ if (rename(fname1, fname2) == 0)
++ return 0;
++#ifdef SUPPORT_FORCE_CHANGE
++ if (force_change && errno == EPERM) {
++ STRUCT_STAT st1, st2;
++ int became_mutable;
++
++ if (x_lstat(fname1, &st1, NULL) != 0)
++ goto failed;
++ became_mutable = make_mutable(fname1, st1.st_mode, st1.st_flags, force_change) > 0;
++ if (became_mutable && rename(fname1, fname2) == 0)
++ goto success;
++ if (x_lstat(fname2, &st2, NULL) == 0
++ && make_mutable(fname2, st2.st_mode, st2.st_flags, force_change) > 0) {
++ if (rename(fname1, fname2) == 0) {
++ success:
++ if (became_mutable) /* Yes, use fname2 and st1! */
++ undo_make_mutable(fname2, st1.st_flags);
++ return 0;
++ }
++ undo_make_mutable(fname2, st2.st_flags);
++ }
++ /* TODO: handle immutable directories */
++ if (became_mutable)
++ undo_make_mutable(fname1, st1.st_flags);
++ failed:
++ errno = EPERM;
++ }
++#endif
++ return -1;
+ }
+
+ void trim_trailing_slashes(char *name)
+diff --git a/t_stub.c b/t_stub.c
+--- a/t_stub.c
++++ b/t_stub.c
+@@ -25,6 +25,7 @@ int modify_window = 0;
+ int module_id = -1;
+ int relative_paths = 0;
+ int module_dirlen = 0;
++int force_change = 0;
+ int preserve_xattrs = 0;
+ mode_t orig_umask = 002;
+ char number_separator = ',';
+@@ -84,3 +85,23 @@ filter_rule_list daemon_filter_list;
+ {
+ return "tester";
+ }
++
++#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
++ int make_mutable(UNUSED(const char *fname), UNUSED(mode_t mode), UNUSED(uint32 fileflags), UNUSED(uint32 iflags))
++{
++ return 0;
++}
++
++/* Undo a prior make_mutable() call that returned a 1. */
++ int undo_make_mutable(UNUSED(const char *fname), UNUSED(uint32 fileflags))
++{
++ return 0;
++}
++#endif
++
++#ifdef SUPPORT_XATTRS
++ int x_lstat(UNUSED(const char *fname), UNUSED(STRUCT_STAT *fst), UNUSED(STRUCT_STAT *xst))
++{
++ return -1;
++}
++#endif
+diff --git a/util.c b/util.c
+--- a/util.c
++++ b/util.c
+@@ -30,6 +30,7 @@ extern int module_id;
+ extern int modify_window;
+ extern int relative_paths;
+ extern int preserve_xattrs;
++extern int force_change;
+ extern char *module_dir;
+ extern unsigned int module_dirlen;
+ extern mode_t orig_umask;
+@@ -123,7 +124,7 @@ NORETURN void overflow_exit(const char *str)
+ exit_cleanup(RERR_MALLOC);
+ }
+
+-int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
++int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags)
+ {
+ #ifndef CAN_SET_SYMLINK_TIMES
+ if (S_ISLNK(mode))
+@@ -140,15 +141,14 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
+ return 0;
+
+ {
++ int ret;
+ #ifdef HAVE_UTIMENSAT
+ struct timespec t[2];
+ t[0].tv_sec = 0;
+ t[0].tv_nsec = UTIME_NOW;
+ t[1].tv_sec = modtime;
+ t[1].tv_nsec = mod_nsec;
+- if (utimensat(AT_FDCWD, fname, t, AT_SYMLINK_NOFOLLOW) < 0)
+- return S_ISLNK(mode) && errno == ENOSYS ? 1 : -1;
+- return 0;
++#define SET_THE_TIME(fn) utimensat(AT_FDCWD, fn, t, AT_SYMLINK_NOFOLLOW)
+ #elif defined HAVE_UTIMES || defined HAVE_LUTIMES
+ struct timeval t[2];
+ t[0].tv_sec = time(NULL);
+@@ -156,25 +156,44 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
+ t[1].tv_sec = modtime;
+ t[1].tv_usec = mod_nsec / 1000;
+ # ifdef HAVE_LUTIMES
+- if (lutimes(fname, t) < 0)
+- return S_ISLNK(mode) && errno == ENOSYS ? 1 : -1;
+- return 0;
++#define SET_THE_TIME(fn) lutimes(fn, t)
+ # else
+- return utimes(fname, t);
++#define SET_THE_TIME(fn) utimes(fn, t)
+ # endif
+ #elif defined HAVE_STRUCT_UTIMBUF
+ struct utimbuf tbuf;
+ tbuf.actime = time(NULL);
+ tbuf.modtime = modtime;
+- return utime(fname,&tbuf);
++#define SET_THE_TIME(fn) utime(fn, &tbuf)
+ #elif defined HAVE_UTIME
+ time_t t[2];
+ t[0] = time(NULL);
+ t[1] = modtime;
+- return utime(fname,t);
++#define SET_THE_TIME(fn) utime(fn, t)
+ #else
+ #error No file-time-modification routine found!
+ #endif
++ ret = SET_THE_TIME(fname);
++ if (ret != 0 && S_ISLNK(mode) && errno == ENOSYS)
++ return 1;
++#ifdef SUPPORT_FORCE_CHANGE
++ if (ret != 0 && force_change && errno == EPERM) {
++ if (fileflags == NO_FFLAGS) {
++ STRUCT_STAT st;
++ if (x_lstat(fname, &st, NULL) == 0)
++ fileflags = st.st_flags;
++ }
++ if (fileflags != NO_FFLAGS
++ && make_mutable(fname, mode, fileflags, force_change) > 0) {
++ ret = SET_THE_TIME(fname);
++ undo_make_mutable(fname, fileflags);
++ }
++ errno = EPERM;
++ }
++#else
++ fileflags = 0; /* avoid compiler warning */
++#endif
++ return ret;
+ }
+ }
+
+diff --git a/xattrs.c b/xattrs.c
+--- a/xattrs.c
++++ b/xattrs.c
+@@ -1033,7 +1033,7 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
+ mode = (fst.st_mode & _S_IFMT) | (fmode & ACCESSPERMS)
+ | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
+ if (fst.st_mode != mode)
+- do_chmod(fname, mode);
++ do_chmod(fname, mode, ST_FLAGS(fst));
+ if (!IS_DEVICE(fst.st_mode))
+ fst.st_rdev = 0; /* just in case */
+