summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/pseudo/pseudo/renameat.patch
blob: a2b2cbc78cd02cf903e673da7b48fe5613dd7082 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
commit 795f2b44b7f692151556782f142a4a6e7d45d892
Author: Peter Seebach <peter.seebach@windriver.com>
Date:   Thu Feb 2 15:49:21 2012 -0600

    Implement renameat()
    
    After three long years, someone tried to use this.  This was impossibly
    hard back when pseudo was written, because there was only one dirfd
    provided for.  Thing is, now, the canonicalization happens in wrapfuncs,
    so a small tweak to makewrappers to recognize that oldpath should use
    olddirfd if it exists is enough to get us fully canonicalized paths
    when needed.
    
    Also fix the crash if base_path gets called with an fd for which we have
    no path.

Upstream-Status: Backport

diff --git a/ChangeLog.txt b/ChangeLog.txt
index 9625b38..25bd463 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,6 +1,9 @@
 2012-02-02:
 	* (seebs) stash dir name for DIR * from opendir using dirfd.
 	* (seebs) add closedir.
+	* (seebs) add initial pass at renameat()
+	* (seebs) in base_path, don't try to strlen the result if
+	  fd_path() returns NULL.
 
 2011-11-02:
 	* (seebs) Call this 1.2 because the UNLOAD change is moderately
diff --git a/makewrappers b/makewrappers
index 20bbf2b..bf344d6 100755
--- a/makewrappers
+++ b/makewrappers
@@ -211,12 +211,13 @@ class Function:
         self.flags = '0'
         self.port = port
         self.directory = ''
-	self.version = 'NULL'
+        self.version = 'NULL'
         # On Darwin, some functions are SECRETLY converted to foo$INODE64
         # when called.  So we have to look those up for real_*
         self.inode64 = None
         self.real_func = None
         self.paths_to_munge = []
+        self.dirfds = {}
         self.hand_wrapped = None
         # used for the copyright date when creating stub functions
         self.date = datetime.date.today().year
@@ -239,6 +240,7 @@ class Function:
         # * If the arg has a name ending in 'path', we will canonicalize it.
         # * If the arg is named 'dirfd' or 'flags', it becomes the default
         #   values for the dirfd and flags arguments when canonicalizing.
+        # * If the name ends in dirfd, we do the same fancy stuff.
         # * Note that the "comments" field (/* ... */ after the decl) can
         #   override the dirfd/flags values.
         self.args = ArgumentList(bits.group(2))
@@ -246,7 +248,9 @@ class Function:
             # ignore varargs, they never get these special treatments
             if arg.vararg:
                 pass
-            elif arg.name == 'dirfd':
+            elif arg.name[-5:] == 'dirfd':
+                if len(arg.name) > 5:
+                    self.dirfds[arg.name[:-5]] = True
                 self.dirfd = 'dirfd'
             elif arg.name == 'flags':
                 self.flags = 'flags'
@@ -325,9 +329,13 @@ class Function:
         """create/allocate canonical paths"""
         alloc_paths = []
         for path in self.paths_to_munge:
+            prefix = path[:-4]
+	    if not prefix in self.dirfds:
+                prefix = ''
+            print "for path %s: prefix <%s>" % ( path, prefix )
             alloc_paths.append(
-                "%s = pseudo_root_path(__func__, __LINE__, %s, %s, %s);" %
-                (path, self.dirfd, path, self.flags))
+                "%s = pseudo_root_path(__func__, __LINE__, %s%s, %s, %s);" %
+                (path, prefix, self.dirfd, path, self.flags))
         return "\n\t\t\t".join(alloc_paths)
 
     def free_paths(self):
diff --git a/ports/unix/guts/renameat.c b/ports/unix/guts/renameat.c
index c8203b7..f13cd1e 100644
--- a/ports/unix/guts/renameat.c
+++ b/ports/unix/guts/renameat.c
@@ -1,15 +1,111 @@
 /* 
- * Copyright (c) 2008-2010 Wind River Systems; see
+ * Copyright (c) 2008-2012 Wind River Systems; see
  * guts/COPYRIGHT for information.
  *
  * static int
  * wrap_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
  *	int rc = -1;
  */
+ 	pseudo_msg_t *msg;
+ 	struct stat oldbuf, newbuf;
+	int oldrc, newrc;
+	int save_errno;
+	int old_db_entry = 0;
 
-	pseudo_diag("help! unimplemented renameat [%s -> %s].\n", oldpath, newpath);
+	pseudo_debug(2, "renameat: %d,%s->%d,%s\n",
+		olddirfd, oldpath ? oldpath : "<nil>",
+		newdirfd, newpath ? newpath : "<nil>");
+
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+	if (olddirfd != AT_FDCWD || newdirfd != AT_FDCWD) {
+		errno = ENOSYS;
+		return -1;
+	}
+#endif
+
+	if (!oldpath || !newpath) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	save_errno = errno;
+
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+	newrc = real_lstat(newpath, &newbuf);
+	oldrc = real_lstat(oldpath, &oldbuf);
+#else
+	oldrc = real___fxstatat(_STAT_VER, olddirfd, oldpath, &oldbuf, AT_SYMLINK_NOFOLLOW);
+	newrc = real___fxstatat(_STAT_VER, newdirfd, newpath, &newbuf, AT_SYMLINK_NOFOLLOW);
+#endif
+
+	errno = save_errno;
+
+	/* newpath must be removed. */
+	/* as with unlink, we have to mark that the file may get deleted */
+	msg = pseudo_client_op_plain(OP_MAY_UNLINK, 0, -1, newdirfd, newpath, newrc ? NULL : &newbuf);
+	if (msg && msg->result == RESULT_SUCCEED)
+		old_db_entry = 1;
 	rc = real_renameat(olddirfd, oldpath, newdirfd, newpath);
+	save_errno = errno;
+	if (old_db_entry) {
+		if (rc == -1) {
+			/* since we failed, that wasn't really unlinked -- put
+			 * it back.
+			 */
+			pseudo_client_op_plain(OP_CANCEL_UNLINK, 0, -1, newdirfd, newpath, &newbuf);
+		} else {
+			/* confirm that the file was removed */
+			pseudo_client_op_plain(OP_DID_UNLINK, 0, -1, newdirfd, newpath, &newbuf);
+		}
+	}
+	if (rc == -1) {
+		/* and we're done. */
+		errno = save_errno;
+		return rc;
+	}
+	save_errno = errno;
+	/* nothing to do for a "rename" of a link to itself */
+	if (newrc != -1 && oldrc != -1 &&
+	    newbuf.st_dev == oldbuf.st_dev &&
+	    newbuf.st_ino == oldbuf.st_ino) {
+		return rc;
+        }
+
+	/* rename(3) is not mv(1).  rename(file, dir) fails; you must provide
+	 * the corrected path yourself.  You can rename over a directory only
+	 * if the source is a directory.  Symlinks are simply removed.
+	 *
+	 * If we got here, the real rename call succeeded.  That means newpath
+	 * has been unlinked and oldpath has been linked to it.
+	 *
+	 * There are a ton of special cases to error check.  I don't check
+	 * for any of them, because in every such case, the underlying rename
+	 * failed, and there is nothing to do.
+	 * The only tricky part is that, because we used to ignore symlinks,
+	 * we may have to rename or remove directory trees even though in
+	 * theory rename can never destroy a directory tree.
+	 */
+	if (!old_db_entry) {
+		/* create an entry under the old name, which will then be
+		 * renamed; this way, children would get renamed too, if there
+		 * were any.
+		 */
+		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_plain(OP_LINK, 0, -1, olddirfd, oldpath, &oldbuf);
+	}
+	/* special case: use 'fd' for olddirfd, because
+	 * we know it has no other meaning for RENAME
+	 */
+	pseudo_client_op_plain(OP_RENAME, 0, olddirfd, newdirfd, newpath, &oldbuf, oldpath);
 
+	errno = save_errno;
 /*	return rc;
  * }
  */
diff --git a/pseudo_client.c b/pseudo_client.c
index 48607c2..4a30420 100644
--- a/pseudo_client.c
+++ b/pseudo_client.c
@@ -988,6 +988,8 @@ base_path(int dirfd, const char *path, int leave_last) {
 		if (dirfd != -1 && dirfd != AT_FDCWD) {
 			if (dirfd >= 0) {
 				basepath = fd_path(dirfd);
+			}
+			if (basepath) {
 				baselen = strlen(basepath);
 			} else {
 				pseudo_diag("got *at() syscall for unknown directory, fd %d\n", dirfd);
@@ -1128,7 +1130,10 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
 	if (path) {
 		pseudo_debug(2, " %s", path);
 	}
-	if (fd != -1) {
+	/* for OP_RENAME in renameat, "fd" is also used for the
+	 * second dirfd.
+	 */
+	if (fd != -1 && op != OP_RENAME) {
 		pseudo_debug(2, " [fd %d]", fd);
 	}
 	if (buf) {