]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/util/fileutil.py
put all private state in $BASEDIR/private
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / util / fileutil.py
1 #  Copyright (c) 2000 Autonomous Zone Industries
2 #  Copyright (c) 2002-2007 Bryce "Zooko" Wilcox-O'Hearn
3 #  This file is licensed under the
4 #    GNU Lesser General Public License v2.1.
5 #    See the file COPYING or visit http://www.gnu.org/ for details.
6 # Portions snarfed out of the Python standard library.
7 # The du part is due to Jim McCoy.
8
9 """
10 Futz with files like a pro.
11 """
12
13 import exceptions, os, stat, tempfile, time
14
15 from twisted.python import log
16
17 def rename(src, dst, tries=4, basedelay=0.1):
18     """ Here is a superkludge to workaround the fact that occasionally on
19     Windows some other process (e.g. an anti-virus scanner, a local search
20     engine, etc.) is looking at your file when you want to delete or move it,
21     and hence you can't.  The horrible workaround is to sit and spin, trying
22     to delete it, for a short time and then give up.
23
24     With the default values of tries and basedelay this can block for less
25     than a second.
26
27     @param tries: number of tries -- each time after the first we wait twice
28     as long as the previous wait
29     @param basedelay: how long to wait before the second try
30     """
31     for i in range(tries-1):
32         try:
33             return os.rename(src, dst)
34         except EnvironmentError, le:
35             # XXX Tighten this to check if this is a permission denied error (possibly due to another Windows process having the file open and execute the superkludge only in this case.
36             log.msg("XXX KLUDGE Attempting to move file %s => %s; got %s; sleeping %s seconds" % (src, dst, le, basedelay,))
37             time.sleep(basedelay)
38             basedelay *= 2
39     return os.rename(src, dst) # The last try.
40
41 def remove(f, tries=4, basedelay=0.1):
42     """ Here is a superkludge to workaround the fact that occasionally on
43     Windows some other process (e.g. an anti-virus scanner, a local search
44     engine, etc.) is looking at your file when you want to delete or move it,
45     and hence you can't.  The horrible workaround is to sit and spin, trying
46     to delete it, for a short time and then give up.
47
48     With the default values of tries and basedelay this can block for less
49     than a second.
50
51     @param tries: number of tries -- each time after the first we wait twice
52     as long as the previous wait
53     @param basedelay: how long to wait before the second try
54     """
55     try:
56         os.chmod(f, stat.S_IWRITE | stat.S_IEXEC | stat.S_IREAD)
57     except:
58         pass
59     for i in range(tries-1):
60         try:
61             return os.remove(f)
62         except EnvironmentError, le:
63             # XXX Tighten this to check if this is a permission denied error (possibly due to another Windows process having the file open and execute the superkludge only in this case.
64             if not os.path.exists(f):
65                 return
66             log.msg("XXX KLUDGE Attempting to remove file %s; got %s; sleeping %s seconds" % (f, le, basedelay,))
67             time.sleep(basedelay)
68             basedelay *= 2
69     return os.remove(f) # The last try.
70
71 class NamedTemporaryDirectory:
72     """
73     This calls tempfile.mkdtemp(), stores the name of the dir in
74     self.name, and rmrf's the dir when it gets garbage collected or
75     "shutdown()".
76     """
77     def __init__(self, cleanup=True, *args, **kwargs):
78         """ If cleanup, then the directory will be rmrf'ed when the object is shutdown. """
79         self.cleanup = cleanup
80         self.name = tempfile.mkdtemp(*args, **kwargs)
81
82     def __repr__(self):
83         return "<%s instance at %x %s>" % (self.__class__.__name__, id(self), self.name)
84
85     def __str__(self):
86         return self.__repr__()
87
88     def __del__(self):
89         try:
90             self.shutdown()
91         except:
92             import traceback
93             traceback.print_exc()
94
95     def shutdown(self):
96         if self.cleanup and hasattr(self, 'name'):
97             rm_dir(self.name)
98
99 def make_dirs(dirname, mode=0777):
100     """
101     An idempotent version of os.makedirs().  If the dir already exists, do
102     nothing and return without raising an exception.  If this call creates the
103     dir, return without raising an exception.  If there is an error that
104     prevents creation or if the directory gets deleted after make_dirs() creates
105     it and before make_dirs() checks that it exists, raise an exception.
106     """
107     tx = None
108     try:
109         os.makedirs(dirname, mode)
110     except OSError, x:
111         tx = x
112
113     if not os.path.isdir(dirname):
114         if tx:
115             raise tx
116         raise exceptions.IOError, "unknown error prevented creation of directory, or deleted the directory immediately after creation: %s" % dirname # careful not to construct an IOError with a 2-tuple, as that has a special meaning...
117
118 def rm_dir(dirname):
119     """
120     A threadsafe and idempotent version of shutil.rmtree().  If the dir is
121     already gone, do nothing and return without raising an exception.  If this
122     call removes the dir, return without raising an exception.  If there is an
123     error that prevents deletion or if the directory gets created again after
124     rm_dir() deletes it and before rm_dir() checks that it is gone, raise an
125     exception.
126     """
127     excs = []
128     try:
129         os.chmod(dirname, stat.S_IWRITE | stat.S_IEXEC | stat.S_IREAD)
130         for f in os.listdir(dirname):
131             fullname = os.path.join(dirname, f)
132             if os.path.isdir(fullname):
133                 rm_dir(fullname)
134             else:
135                 remove(fullname)
136         os.rmdir(dirname)
137     except Exception, le:
138         # Ignore "No such file or directory"
139         if (not isinstance(le, OSError)) or le.args[0] != 2:
140             excs.append(le)
141
142     # Okay, now we've recursively removed everything, ignoring any "No
143     # such file or directory" errors, and collecting any other errors.
144
145     if os.path.exists(dirname):
146         if len(excs) == 1:
147             raise excs[0]
148         if len(excs) == 0:
149             raise OSError, "Failed to remove dir for unknown reason."
150         raise OSError, excs
151
152
153 def remove_if_possible(f):
154     try:
155         remove(f)
156     except:
157         pass
158
159 def open_or_create(fname, binarymode=True):
160     try:
161         return open(fname, binarymode and "r+b" or "r+")
162     except EnvironmentError:
163         return open(fname, binarymode and "w+b" or "w+")
164
165
166 def du(basedir):
167     size = 0
168
169     for root, dirs, files in os.walk(basedir):
170         for f in files:
171             fn = os.path.join(root, f)
172             size += os.path.getsize(fn)
173
174     return size