aboutsummaryrefslogtreecommitdiffstats
path: root/guts
diff options
context:
space:
mode:
authorPeter Seebach <peter.seebach@windriver.com>2010-04-26 16:40:55 -0700
committerPeter Seebach <peter.seebach@windriver.com>2010-04-26 17:13:45 -0700
commitfbaffe9e22c6eac916f2323b5c2940f76862f9cb (patch)
tree95edeb9f97f5e17fca8081864523aa3b31dd78d0 /guts
parent69de8f0dfdd47fb70487f0d8213e5659e9b7e2c6 (diff)
downloadpseudo-fbaffe9e22c6eac916f2323b5c2940f76862f9cb.tar.gz
pseudo-fbaffe9e22c6eac916f2323b5c2940f76862f9cb.tar.bz2
pseudo-fbaffe9e22c6eac916f2323b5c2940f76862f9cb.zip
Handle rename(3) across devices.
When you rename across devices, inode can change. Until now, pseudo had no tools for handling a change in inode, but this is clearly a legitimate case.
Diffstat (limited to 'guts')
-rw-r--r--guts/rename.c41
1 files changed, 29 insertions, 12 deletions
diff --git a/guts/rename.c b/guts/rename.c
index 9a27538..973b164 100644
--- a/guts/rename.c
+++ b/guts/rename.c
@@ -31,18 +31,21 @@
/* as with unlink, we have to do the remove before the operation
*/
msg = pseudo_client_op(OP_UNLINK, 0, -1, -1, newpath, newrc ? NULL : &newbuf);
+ /* stash the server's old data */
rc = real_rename(oldpath, newpath);
save_errno = errno;
if (rc == -1) {
- newbuf.st_uid = msg->uid;
- newbuf.st_gid = msg->uid;
- newbuf.st_mode = msg->mode;
- newbuf.st_dev = msg->dev;
- newbuf.st_ino = msg->ino;
- /* since we failed, that wasn't really unlinked -- put
- * it back.
- */
- pseudo_client_op(OP_LINK, 0, -1, -1, newpath, &newbuf);
+ if (msg && msg->result == RESULT_SUCCEED) {
+ newbuf.st_uid = msg->uid;
+ newbuf.st_gid = msg->uid;
+ newbuf.st_mode = msg->mode;
+ newbuf.st_dev = msg->dev;
+ newbuf.st_ino = msg->ino;
+ /* since we failed, that wasn't really unlinked -- put
+ * it back.
+ */
+ pseudo_client_op(OP_LINK, 0, -1, -1, newpath, &newbuf);
+ }
/* and we're done. */
errno = save_errno;
return rc;
@@ -70,17 +73,31 @@
* theory rename can never destroy a directory tree.
*/
- /* fill in "correct" details from server */
- msg = pseudo_client_op(OP_STAT, 0, -1, -1, oldpath, &oldbuf);
+ /* re-stat the new file. Why? Because if something got moved
+ * across device boundaries, its dev/ino changed!
+ */
+ newrc = real___lxstat64(_STAT_VER, newpath, &newbuf);
if (msg && msg->result == RESULT_SUCCEED) {
pseudo_stat_msg(&oldbuf, msg);
+ if (newrc == 0) {
+ if (newbuf.st_dev != oldbuf.st_dev) {
+ oldbuf.st_dev = newbuf.st_dev;
+ oldbuf.st_ino = newbuf.st_ino;
+ }
+ }
pseudo_debug(1, "renaming %s, got old mode of 0%o\n", oldpath, (int) msg->mode);
} else {
/* create an entry under the old name, which will then be
* renamed; this way, children would get renamed too, if there
* were any.
*/
- pseudo_debug(1, "renaming new '%s' [%llu]\n",
+ if (newrc == 0) {
+ if (newbuf.st_dev != oldbuf.st_dev) {
+ oldbuf.st_dev = newbuf.st_dev;
+ oldbuf.st_ino = newbuf.st_ino;
+ }
+ }
+ pseudo_debug(1, "creating new '%s' [%llu] to rename\n",
oldpath, (unsigned long long) oldbuf.st_ino);
pseudo_client_op(OP_LINK, 0, -1, -1, oldpath, &oldbuf);
}