Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/cmdutil.py @ 27370:d9e3ebe56970 stable
record: don't dereference symlinks while copying over stat data
Previously, we could be calling os.utime or os.chflags (via shutil.copystat) on
a symlink. These functions dereference symlinks, so this would have caused the
timestamp of the target to be set. On a read-only or similarly weird
filesystem, this might cause an exception to be raised.
This is pretty hard to test because conjuring up a read-only filesystem for
test purposes is non-trivial.
author | Siddharth Agarwal <sid0@fb.com> |
---|---|
date | Sat, 12 Dec 2015 10:58:05 -0800 |
parents | 1aee2ab0f902 |
children | 4eeef1b2d689 84d686cb62c4 |
comparison
equal
deleted
inserted
replaced
27369:c48ecc0b5bc9 | 27370:d9e3ebe56970 |
---|---|
5 # This software may be used and distributed according to the terms of the | 5 # This software may be used and distributed according to the terms of the |
6 # GNU General Public License version 2 or any later version. | 6 # GNU General Public License version 2 or any later version. |
7 | 7 |
8 from node import hex, bin, nullid, nullrev, short | 8 from node import hex, bin, nullid, nullrev, short |
9 from i18n import _ | 9 from i18n import _ |
10 import os, sys, errno, re, tempfile, cStringIO, shutil | 10 import os, sys, errno, re, tempfile, cStringIO |
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies | 11 import util, scmutil, templater, patch, error, templatekw, revlog, copies |
12 import match as matchmod | 12 import match as matchmod |
13 import repair, graphmod, revset, phases, obsolete, pathutil | 13 import repair, graphmod, revset, phases, obsolete, pathutil |
14 import changelog | 14 import changelog |
15 import bookmarks | 15 import bookmarks |
164 for f in tobackup: | 164 for f in tobackup: |
165 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.', | 165 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.', |
166 dir=backupdir) | 166 dir=backupdir) |
167 os.close(fd) | 167 os.close(fd) |
168 ui.debug('backup %r as %r\n' % (f, tmpname)) | 168 ui.debug('backup %r as %r\n' % (f, tmpname)) |
169 util.copyfile(repo.wjoin(f), tmpname) | 169 util.copyfile(repo.wjoin(f), tmpname, copystat=True) |
170 shutil.copystat(repo.wjoin(f), tmpname) | |
171 backups[f] = tmpname | 170 backups[f] = tmpname |
172 | 171 |
173 fp = cStringIO.StringIO() | 172 fp = cStringIO.StringIO() |
174 for c in chunks: | 173 for c in chunks: |
175 fname = c.filename() | 174 fname = c.filename() |
214 # without normallookup, restoring timestamp | 213 # without normallookup, restoring timestamp |
215 # may cause partially committed files | 214 # may cause partially committed files |
216 # to be treated as unmodified | 215 # to be treated as unmodified |
217 dirstate.normallookup(realname) | 216 dirstate.normallookup(realname) |
218 | 217 |
219 util.copyfile(tmpname, repo.wjoin(realname)) | 218 # copystat=True here and above are a hack to trick any |
220 # Our calls to copystat() here and above are a | 219 # editors that have f open that we haven't modified them. |
221 # hack to trick any editors that have f open that | |
222 # we haven't modified them. | |
223 # | 220 # |
224 # Also note that this racy as an editor could | 221 # Also note that this racy as an editor could notice the |
225 # notice the file's mtime before we've finished | 222 # file's mtime before we've finished writing it. |
226 # writing it. | 223 util.copyfile(tmpname, repo.wjoin(realname), copystat=True) |
227 shutil.copystat(tmpname, repo.wjoin(realname)) | |
228 os.unlink(tmpname) | 224 os.unlink(tmpname) |
229 if tobackup: | 225 if tobackup: |
230 os.rmdir(backupdir) | 226 os.rmdir(backupdir) |
231 except OSError: | 227 except OSError: |
232 pass | 228 pass |