1421ccda3079077dd613308526e02d797f5cc356aHess Chen## @file
2421ccda3079077dd613308526e02d797f5cc356aHess Chen# This file hooks file and directory creation and removal
3421ccda3079077dd613308526e02d797f5cc356aHess Chen#
4421ccda3079077dd613308526e02d797f5cc356aHess Chen# Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>
5421ccda3079077dd613308526e02d797f5cc356aHess Chen#
6421ccda3079077dd613308526e02d797f5cc356aHess Chen# This program and the accompanying materials are licensed and made available
7421ccda3079077dd613308526e02d797f5cc356aHess Chen# under the terms and conditions of the BSD License which accompanies this
8421ccda3079077dd613308526e02d797f5cc356aHess Chen# distribution. The full text of the license may be found at
9421ccda3079077dd613308526e02d797f5cc356aHess Chen# http://opensource.org/licenses/bsd-license.php
10421ccda3079077dd613308526e02d797f5cc356aHess Chen#
11421ccda3079077dd613308526e02d797f5cc356aHess Chen# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12421ccda3079077dd613308526e02d797f5cc356aHess Chen# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13421ccda3079077dd613308526e02d797f5cc356aHess Chen#
14421ccda3079077dd613308526e02d797f5cc356aHess Chen
15421ccda3079077dd613308526e02d797f5cc356aHess Chen'''
16421ccda3079077dd613308526e02d797f5cc356aHess ChenFile hook
17421ccda3079077dd613308526e02d797f5cc356aHess Chen'''
18421ccda3079077dd613308526e02d797f5cc356aHess Chen
19421ccda3079077dd613308526e02d797f5cc356aHess Chenimport os
20421ccda3079077dd613308526e02d797f5cc356aHess Chenimport stat
21421ccda3079077dd613308526e02d797f5cc356aHess Chenimport time
22421ccda3079077dd613308526e02d797f5cc356aHess Chenimport zipfile
23421ccda3079077dd613308526e02d797f5cc356aHess Chenfrom time import sleep
24421ccda3079077dd613308526e02d797f5cc356aHess Chenfrom Library import GlobalData
25421ccda3079077dd613308526e02d797f5cc356aHess Chen
26421ccda3079077dd613308526e02d797f5cc356aHess Chen__built_in_remove__ = os.remove
27421ccda3079077dd613308526e02d797f5cc356aHess Chen__built_in_mkdir__  = os.mkdir
28421ccda3079077dd613308526e02d797f5cc356aHess Chen__built_in_rmdir__  = os.rmdir
29421ccda3079077dd613308526e02d797f5cc356aHess Chen__built_in_chmod__  = os.chmod
30421ccda3079077dd613308526e02d797f5cc356aHess Chen__built_in_open__   = open
31421ccda3079077dd613308526e02d797f5cc356aHess Chen
32421ccda3079077dd613308526e02d797f5cc356aHess Chen_RMFILE      = 0
33421ccda3079077dd613308526e02d797f5cc356aHess Chen_MKFILE      = 1
34421ccda3079077dd613308526e02d797f5cc356aHess Chen_RMDIR       = 2
35421ccda3079077dd613308526e02d797f5cc356aHess Chen_MKDIR       = 3
36421ccda3079077dd613308526e02d797f5cc356aHess Chen_CHMOD       = 4
37421ccda3079077dd613308526e02d797f5cc356aHess Chen
38421ccda3079077dd613308526e02d797f5cc356aHess ChengBACKUPFILE = 'file.backup'
39421ccda3079077dd613308526e02d797f5cc356aHess ChengEXCEPTION_LIST = ['Conf'+os.sep+'DistributionPackageDatabase.db', '.tmp', gBACKUPFILE]
40421ccda3079077dd613308526e02d797f5cc356aHess Chen
41421ccda3079077dd613308526e02d797f5cc356aHess Chenclass _PathInfo:
42421ccda3079077dd613308526e02d797f5cc356aHess Chen    def __init__(self, action, path, mode=-1):
43421ccda3079077dd613308526e02d797f5cc356aHess Chen        self.action = action
44421ccda3079077dd613308526e02d797f5cc356aHess Chen        self.path = path
45421ccda3079077dd613308526e02d797f5cc356aHess Chen        self.mode = mode
46421ccda3079077dd613308526e02d797f5cc356aHess Chen
47421ccda3079077dd613308526e02d797f5cc356aHess Chenclass RecoverMgr:
48421ccda3079077dd613308526e02d797f5cc356aHess Chen    def __init__(self, workspace):
49421ccda3079077dd613308526e02d797f5cc356aHess Chen        self.rlist = []
50421ccda3079077dd613308526e02d797f5cc356aHess Chen        self.zip = None
51421ccda3079077dd613308526e02d797f5cc356aHess Chen        self.workspace = os.path.normpath(workspace)
52421ccda3079077dd613308526e02d797f5cc356aHess Chen        self.backupfile = gBACKUPFILE
53421ccda3079077dd613308526e02d797f5cc356aHess Chen        self.zipfile = os.path.join(self.workspace, gBACKUPFILE)
54421ccda3079077dd613308526e02d797f5cc356aHess Chen
55421ccda3079077dd613308526e02d797f5cc356aHess Chen    def _createzip(self):
56421ccda3079077dd613308526e02d797f5cc356aHess Chen        if self.zip:
57421ccda3079077dd613308526e02d797f5cc356aHess Chen            return
58421ccda3079077dd613308526e02d797f5cc356aHess Chen        self.zip = zipfile.ZipFile(self.zipfile, 'w', zipfile.ZIP_DEFLATED)
59421ccda3079077dd613308526e02d797f5cc356aHess Chen
60421ccda3079077dd613308526e02d797f5cc356aHess Chen    def _save(self, tmp, path):
61421ccda3079077dd613308526e02d797f5cc356aHess Chen        if not self._tryhook(path):
62421ccda3079077dd613308526e02d797f5cc356aHess Chen            return
63421ccda3079077dd613308526e02d797f5cc356aHess Chen        self.rlist.append(_PathInfo(tmp, path))
64421ccda3079077dd613308526e02d797f5cc356aHess Chen
65421ccda3079077dd613308526e02d797f5cc356aHess Chen    def bkrmfile(self, path):
66421ccda3079077dd613308526e02d797f5cc356aHess Chen        arc = self._tryhook(path)
67421ccda3079077dd613308526e02d797f5cc356aHess Chen        if arc and os.path.isfile(path):
68421ccda3079077dd613308526e02d797f5cc356aHess Chen            self._createzip()
69421ccda3079077dd613308526e02d797f5cc356aHess Chen            self.zip.write(path, arc.encode('utf_8'))
70421ccda3079077dd613308526e02d797f5cc356aHess Chen            sta = os.stat(path)
71421ccda3079077dd613308526e02d797f5cc356aHess Chen            oldmode = stat.S_IMODE(sta.st_mode)
72421ccda3079077dd613308526e02d797f5cc356aHess Chen            self.rlist.append(_PathInfo(_CHMOD, path, oldmode))
73421ccda3079077dd613308526e02d797f5cc356aHess Chen            self.rlist.append(_PathInfo(_RMFILE, path))
74421ccda3079077dd613308526e02d797f5cc356aHess Chen        __built_in_remove__(path)
75421ccda3079077dd613308526e02d797f5cc356aHess Chen
76421ccda3079077dd613308526e02d797f5cc356aHess Chen    def bkmkfile(self, path, mode, bufsize):
77421ccda3079077dd613308526e02d797f5cc356aHess Chen        if not os.path.exists(path):
78421ccda3079077dd613308526e02d797f5cc356aHess Chen            self._save(_MKFILE, path)
79421ccda3079077dd613308526e02d797f5cc356aHess Chen        return __built_in_open__(path, mode, bufsize)
80421ccda3079077dd613308526e02d797f5cc356aHess Chen
81421ccda3079077dd613308526e02d797f5cc356aHess Chen    def bkrmdir(self, path):
82421ccda3079077dd613308526e02d797f5cc356aHess Chen        if os.path.exists(path):
83421ccda3079077dd613308526e02d797f5cc356aHess Chen            sta = os.stat(path)
84421ccda3079077dd613308526e02d797f5cc356aHess Chen            oldmode = stat.S_IMODE(sta.st_mode)
85421ccda3079077dd613308526e02d797f5cc356aHess Chen            self.rlist.append(_PathInfo(_CHMOD, path, oldmode))
86421ccda3079077dd613308526e02d797f5cc356aHess Chen            self._save(_RMDIR, path)
87421ccda3079077dd613308526e02d797f5cc356aHess Chen        __built_in_rmdir__(path)
88421ccda3079077dd613308526e02d797f5cc356aHess Chen
89421ccda3079077dd613308526e02d797f5cc356aHess Chen    def bkmkdir(self, path, mode):
90421ccda3079077dd613308526e02d797f5cc356aHess Chen        if not os.path.exists(path):
91421ccda3079077dd613308526e02d797f5cc356aHess Chen            self._save(_MKDIR, path)
92421ccda3079077dd613308526e02d797f5cc356aHess Chen        __built_in_mkdir__(path, mode)
93421ccda3079077dd613308526e02d797f5cc356aHess Chen
94421ccda3079077dd613308526e02d797f5cc356aHess Chen    def bkchmod(self, path, mode):
95421ccda3079077dd613308526e02d797f5cc356aHess Chen        if self._tryhook(path) and os.path.exists(path):
96421ccda3079077dd613308526e02d797f5cc356aHess Chen            sta = os.stat(path)
97421ccda3079077dd613308526e02d797f5cc356aHess Chen            oldmode = stat.S_IMODE(sta.st_mode)
98421ccda3079077dd613308526e02d797f5cc356aHess Chen            self.rlist.append(_PathInfo(_CHMOD, path, oldmode))
99421ccda3079077dd613308526e02d797f5cc356aHess Chen        __built_in_chmod__(path, mode)
100421ccda3079077dd613308526e02d797f5cc356aHess Chen
101421ccda3079077dd613308526e02d797f5cc356aHess Chen    def rollback(self):
102421ccda3079077dd613308526e02d797f5cc356aHess Chen        if self.zip:
103421ccda3079077dd613308526e02d797f5cc356aHess Chen            self.zip.close()
104421ccda3079077dd613308526e02d797f5cc356aHess Chen            self.zip = None
105421ccda3079077dd613308526e02d797f5cc356aHess Chen        index = len(self.rlist) - 1
106421ccda3079077dd613308526e02d797f5cc356aHess Chen        while index >= 0:
107421ccda3079077dd613308526e02d797f5cc356aHess Chen            item = self.rlist[index]
108421ccda3079077dd613308526e02d797f5cc356aHess Chen            exist = os.path.exists(item.path)
109421ccda3079077dd613308526e02d797f5cc356aHess Chen            if item.action == _MKFILE and exist:
110421ccda3079077dd613308526e02d797f5cc356aHess Chen                #if not os.access(item.path, os.W_OK):
111421ccda3079077dd613308526e02d797f5cc356aHess Chen                #    os.chmod(item.path, S_IWUSR)
112421ccda3079077dd613308526e02d797f5cc356aHess Chen                __built_in_remove__(item.path)
113421ccda3079077dd613308526e02d797f5cc356aHess Chen            elif item.action == _RMFILE and not exist:
114421ccda3079077dd613308526e02d797f5cc356aHess Chen                if not self.zip:
115421ccda3079077dd613308526e02d797f5cc356aHess Chen                    self.zip = zipfile.ZipFile(self.zipfile, 'r', zipfile.ZIP_DEFLATED)
116421ccda3079077dd613308526e02d797f5cc356aHess Chen                arcname = os.path.normpath(item.path)
117421ccda3079077dd613308526e02d797f5cc356aHess Chen                arcname = arcname[len(self.workspace)+1:].encode('utf_8')
118421ccda3079077dd613308526e02d797f5cc356aHess Chen                if os.sep != "/" and os.sep in arcname:
119421ccda3079077dd613308526e02d797f5cc356aHess Chen                    arcname = arcname.replace(os.sep, '/')
120421ccda3079077dd613308526e02d797f5cc356aHess Chen                mtime = self.zip.getinfo(arcname).date_time
121421ccda3079077dd613308526e02d797f5cc356aHess Chen                content = self.zip.read(arcname)
122421ccda3079077dd613308526e02d797f5cc356aHess Chen                filep = __built_in_open__(item.path, "wb")
123421ccda3079077dd613308526e02d797f5cc356aHess Chen                filep.write(content)
124421ccda3079077dd613308526e02d797f5cc356aHess Chen                filep.close()
125421ccda3079077dd613308526e02d797f5cc356aHess Chen                intime = time.mktime(mtime + (0, 0, 0))
126421ccda3079077dd613308526e02d797f5cc356aHess Chen                os.utime(item.path, (intime, intime))
127421ccda3079077dd613308526e02d797f5cc356aHess Chen            elif item.action == _MKDIR and exist:
128421ccda3079077dd613308526e02d797f5cc356aHess Chen                while True:
129421ccda3079077dd613308526e02d797f5cc356aHess Chen                    try:
130421ccda3079077dd613308526e02d797f5cc356aHess Chen                        __built_in_rmdir__(item.path)
131421ccda3079077dd613308526e02d797f5cc356aHess Chen                        break
132421ccda3079077dd613308526e02d797f5cc356aHess Chen                    except IOError:
133421ccda3079077dd613308526e02d797f5cc356aHess Chen                        # Sleep a short time and try again
134421ccda3079077dd613308526e02d797f5cc356aHess Chen                        # The anti-virus software may delay the file removal in this directory
135421ccda3079077dd613308526e02d797f5cc356aHess Chen                        sleep(0.1)
136421ccda3079077dd613308526e02d797f5cc356aHess Chen            elif item.action == _RMDIR and not exist:
137421ccda3079077dd613308526e02d797f5cc356aHess Chen                __built_in_mkdir__(item.path)
138421ccda3079077dd613308526e02d797f5cc356aHess Chen            elif item.action == _CHMOD and exist:
139421ccda3079077dd613308526e02d797f5cc356aHess Chen                try:
140421ccda3079077dd613308526e02d797f5cc356aHess Chen                    __built_in_chmod__(item.path, item.mode)
141421ccda3079077dd613308526e02d797f5cc356aHess Chen                except EnvironmentError:
142421ccda3079077dd613308526e02d797f5cc356aHess Chen                    pass
143421ccda3079077dd613308526e02d797f5cc356aHess Chen            index -= 1
144421ccda3079077dd613308526e02d797f5cc356aHess Chen        self.commit()
145421ccda3079077dd613308526e02d797f5cc356aHess Chen
146421ccda3079077dd613308526e02d797f5cc356aHess Chen    def commit(self):
147421ccda3079077dd613308526e02d797f5cc356aHess Chen        if self.zip:
148421ccda3079077dd613308526e02d797f5cc356aHess Chen            self.zip.close()
149421ccda3079077dd613308526e02d797f5cc356aHess Chen            __built_in_remove__(self.zipfile)
150421ccda3079077dd613308526e02d797f5cc356aHess Chen
151421ccda3079077dd613308526e02d797f5cc356aHess Chen    # Check if path needs to be hooked
152421ccda3079077dd613308526e02d797f5cc356aHess Chen    def _tryhook(self, path):
153421ccda3079077dd613308526e02d797f5cc356aHess Chen        path = os.path.normpath(path)
154421ccda3079077dd613308526e02d797f5cc356aHess Chen        works = self.workspace if str(self.workspace).endswith(os.sep) else (self.workspace  + os.sep)
155421ccda3079077dd613308526e02d797f5cc356aHess Chen        if not path.startswith(works):
156421ccda3079077dd613308526e02d797f5cc356aHess Chen            return ''
157421ccda3079077dd613308526e02d797f5cc356aHess Chen        for exceptdir in gEXCEPTION_LIST:
158421ccda3079077dd613308526e02d797f5cc356aHess Chen            full = os.path.join(self.workspace, exceptdir)
159421ccda3079077dd613308526e02d797f5cc356aHess Chen            if full == path or path.startswith(full + os.sep) or os.path.split(full)[0] == path:
160421ccda3079077dd613308526e02d797f5cc356aHess Chen                return ''
161421ccda3079077dd613308526e02d797f5cc356aHess Chen        return path[len(self.workspace)+1:]
162421ccda3079077dd613308526e02d797f5cc356aHess Chen
163421ccda3079077dd613308526e02d797f5cc356aHess Chendef _hookrm(path):
164421ccda3079077dd613308526e02d797f5cc356aHess Chen    if GlobalData.gRECOVERMGR:
165421ccda3079077dd613308526e02d797f5cc356aHess Chen        GlobalData.gRECOVERMGR.bkrmfile(path)
166421ccda3079077dd613308526e02d797f5cc356aHess Chen    else:
167421ccda3079077dd613308526e02d797f5cc356aHess Chen        __built_in_remove__(path)
168421ccda3079077dd613308526e02d797f5cc356aHess Chen
169421ccda3079077dd613308526e02d797f5cc356aHess Chendef _hookmkdir(path, mode=0777):
170421ccda3079077dd613308526e02d797f5cc356aHess Chen    if GlobalData.gRECOVERMGR:
171421ccda3079077dd613308526e02d797f5cc356aHess Chen        GlobalData.gRECOVERMGR.bkmkdir(path, mode)
172421ccda3079077dd613308526e02d797f5cc356aHess Chen    else:
173421ccda3079077dd613308526e02d797f5cc356aHess Chen        __built_in_mkdir__(path, mode)
174421ccda3079077dd613308526e02d797f5cc356aHess Chen
175421ccda3079077dd613308526e02d797f5cc356aHess Chendef _hookrmdir(path):
176421ccda3079077dd613308526e02d797f5cc356aHess Chen    if GlobalData.gRECOVERMGR:
177421ccda3079077dd613308526e02d797f5cc356aHess Chen        GlobalData.gRECOVERMGR.bkrmdir(path)
178421ccda3079077dd613308526e02d797f5cc356aHess Chen    else:
179421ccda3079077dd613308526e02d797f5cc356aHess Chen        __built_in_rmdir__(path)
180421ccda3079077dd613308526e02d797f5cc356aHess Chen
181421ccda3079077dd613308526e02d797f5cc356aHess Chendef _hookmkfile(path, mode='r', bufsize=-1):
182421ccda3079077dd613308526e02d797f5cc356aHess Chen    if GlobalData.gRECOVERMGR:
183421ccda3079077dd613308526e02d797f5cc356aHess Chen        return GlobalData.gRECOVERMGR.bkmkfile(path, mode, bufsize)
184421ccda3079077dd613308526e02d797f5cc356aHess Chen    return __built_in_open__(path, mode, bufsize)
185421ccda3079077dd613308526e02d797f5cc356aHess Chen
186421ccda3079077dd613308526e02d797f5cc356aHess Chendef _hookchmod(path, mode):
187421ccda3079077dd613308526e02d797f5cc356aHess Chen    if GlobalData.gRECOVERMGR:
188421ccda3079077dd613308526e02d797f5cc356aHess Chen        GlobalData.gRECOVERMGR.bkchmod(path, mode)
189421ccda3079077dd613308526e02d797f5cc356aHess Chen    else:
190421ccda3079077dd613308526e02d797f5cc356aHess Chen        __built_in_chmod__(path, mode)
191421ccda3079077dd613308526e02d797f5cc356aHess Chen
192421ccda3079077dd613308526e02d797f5cc356aHess Chendef SetRecoverMgr(mgr):
193421ccda3079077dd613308526e02d797f5cc356aHess Chen    GlobalData.gRECOVERMGR = mgr
194421ccda3079077dd613308526e02d797f5cc356aHess Chen
195421ccda3079077dd613308526e02d797f5cc356aHess Chenos.remove   = _hookrm
196421ccda3079077dd613308526e02d797f5cc356aHess Chenos.mkdir    = _hookmkdir
197421ccda3079077dd613308526e02d797f5cc356aHess Chenos.rmdir    = _hookrmdir
198421ccda3079077dd613308526e02d797f5cc356aHess Chenos.chmod    = _hookchmod
199421ccda3079077dd613308526e02d797f5cc356aHess Chen__FileHookOpen__    = _hookmkfile
200