1 package com.trendmicro.grid.acl.ds.cifs;
2
3 import jcifs.smb.SmbException;
4 import jcifs.smb.SmbFile;
5 import jcifs.smb.SmbFileInputStream;
6 import jcifs.smb.SmbFileOutputStream;
7 import jcifs.util.transport.TransportException;
8 import net.sf.tinyjee.config.Connection;
9 import net.sf.tinyjee.config.PropertySection;
10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory;
12 import org.springframework.stereotype.Repository;
13
14 import java.io.*;
15 import java.net.MalformedURLException;
16 import java.net.UnknownHostException;
17 import java.security.NoSuchAlgorithmException;
18 import java.util.Collection;
19 import java.util.Date;
20
21 import static com.trendmicro.grid.acl.ds.cifs.CIFSPropertySection.*;
22 import static jcifs.smb.NtStatus.NT_STATUS_NO_SUCH_FILE;
23 import static jcifs.smb.NtStatus.NT_STATUS_OBJECT_PATH_NOT_FOUND;
24
25
26
27
28
29
30
31 @Repository
32 public class CIFSFileRepository extends AbstractStructuredFileRepository {
33
34 private static final Logger log = LoggerFactory.getLogger(CIFSFileRepository.class);
35
36
37
38
39 public static final int NT_NOT_SAME_DEVICE = 0xC00000D4;
40
41 private CIFSLoginHandler loginHandler;
42
43 public CIFSFileRepository() {
44 }
45
46 public CIFSFileRepository(String hostname, String pathPrefix, String domain, String username, String password) {
47 Collection<Connection> connections = getCIFSConnections();
48 connections.clear();
49 connections.add(createCIFSConnection(hostname, pathPrefix, domain, username, password));
50 }
51
52
53
54
55
56 @Override
57 protected void initialize(PropertySection section) throws NoSuchAlgorithmException {
58 super.initialize(section);
59
60 for (Connection connection : getCIFSConnections()) {
61
62 final String hostname = connection.getAddressValue();
63 String pathPrefix = connection.getPropertyValue(KEY_CONNECTION_PATH_PREFIX, "").replace('\\', '/');
64 if (!pathPrefix.isEmpty() && !pathPrefix.endsWith("/"))
65 pathPrefix = pathPrefix.concat("/");
66
67
68 if (hostname == null || hostname.isEmpty()) {
69 log.error("TMACL-01410:No cifs host was defined, CIFS file repository will be unavailable.");
70 continue;
71 }
72
73 loginHandler = new CIFSLoginHandler(hostname, pathPrefix);
74
75 String domain = connection.getPropertyValue(KEY_CONNECTION_DOMAIN, null);
76 String username = connection.getPropertyValue(KEY_CONNECTION_USERNAME, null);
77 String password = connection.getPropertyValue(KEY_CONNECTION_PASSWORD, "");
78 loginHandler.loginAsynchronously(domain, username, password);
79
80 break;
81 }
82 }
83
84
85
86
87
88
89 static void fixExceptionChain(Throwable e) {
90 if (e == null)
91 return;
92
93 if (e instanceof SmbException) {
94 SmbException se = (SmbException) e;
95 if (se.getRootCause() != null && se.getCause() == null)
96 se.initCause(se.getRootCause());
97 } else if (e instanceof TransportException) {
98 TransportException te = (TransportException) e;
99 if (te.getRootCause() != null && te.getCause() == null)
100 te.initCause(te.getRootCause());
101 }
102
103 fixExceptionChain(e.getCause());
104 }
105
106 private static FileNotFoundException createFileNotFound(Throwable cause) throws FileNotFoundException {
107 fixExceptionChain(cause);
108 FileNotFoundException e = new FileNotFoundException(cause.getMessage());
109 e.initCause(cause);
110 return e;
111 }
112
113
114
115
116 @Override
117 protected String getBaseURI(String baseURI) {
118 try {
119 return baseURI.isEmpty() ? loginHandler.getAuthenticatedRepositoryBaseURI() : baseURI;
120 } catch (Exception e) {
121 throw new RuntimeException(e);
122 }
123 }
124
125 private SmbFile toTargetFile(String path, boolean createPathIfMissing) throws FileNotFoundException {
126 try {
127 final SmbFile basePath = loginHandler.getAuthenticatedRepositoryBasePath();
128 final SmbFile file = new SmbFile(basePath, path);
129
130 if (createPathIfMissing) {
131 String parent = file.getParent();
132 SmbFile parentFile = new SmbFile(basePath, parent);
133 if (!parentFile.isDirectory()) {
134 if (log.isTraceEnabled())
135 log.trace("Creating directory on CIFS host with address '{}'", parentFile.getURL());
136 parentFile.mkdirs();
137 }
138 }
139
140 return file;
141
142 } catch (MalformedURLException e) {
143 throw createFileNotFound(e);
144 } catch (UnknownHostException e) {
145 loginHandler.forceReLogin();
146 throw createFileNotFound(e);
147 } catch (SmbException e) {
148 loginHandler.forceReLogin();
149 throw createFileNotFound(e);
150 }
151 }
152
153
154
155
156 @Override
157 protected boolean isExisting(String path) {
158 try {
159
160 return !"".equals(path) && toTargetFile(path, false).isFile();
161 } catch (Exception e) {
162 return false;
163 }
164 }
165
166
167
168
169 @Override
170 protected Date getLastModified(String path) {
171 try {
172 if (path.equals(""))
173 return null;
174 long lm = toTargetFile(path, false).lastModified();
175 return lm == 0L ? null : new Date(lm);
176 } catch (Exception e) {
177 return null;
178 }
179 }
180
181
182
183
184 @Override
185 protected OutputStream openForWrite(String path)
186 throws IllegalStateException, FileNotFoundException {
187 try {
188 return new BufferedOutputStream(new SmbFileOutputStream(toTargetFile(path, true)));
189 } catch (MalformedURLException e) {
190 log.error("TMACL-01230:Can't open '" + path + "' for writing, the path is invalid.", e);
191 throw createFileNotFound(e);
192 } catch (UnknownHostException e) {
193 throw createFileNotFound(e);
194 } catch (SmbException e) {
195 log.error("TMACL-01240:Can't open '" + path + "' for writing.", e);
196 throw createFileNotFound(e);
197 }
198 }
199
200
201
202
203 @Override
204 protected InputStream openForRead(String path) throws FileNotFoundException {
205 try {
206 return new BufferedInputStream(new SmbFileInputStream(toTargetFile(path, false)));
207 } catch (MalformedURLException e) {
208 log.error("TMACL-01250:Can't open '" + path + "' for reading, the path is invalid.", e);
209 throw createFileNotFound(e);
210 } catch (UnknownHostException e) {
211 throw createFileNotFound(e);
212 } catch (SmbException e) {
213 if (e.getNtStatus() == NT_STATUS_NO_SUCH_FILE || e.getNtStatus() == NT_STATUS_OBJECT_PATH_NOT_FOUND) {
214 log.error("TMACL-01261:Can't open '" + path + "' for reading, the file does not exist.");
215 throw new FileNotFoundException(e.getMessage());
216 } else
217 log.error("TMACL-01260:Can't open '" + path + "' for reading.", e);
218 throw createFileNotFound(e);
219 }
220 }
221
222
223
224
225 @Override
226 protected void rename(String fromPath, String toPath) throws FileNotFoundException {
227 SmbFile from = toTargetFile(fromPath, false);
228 try {
229 if (from.isFile()) {
230 SmbFile to = toTargetFile(toPath, true);
231 try {
232 from.renameTo(to);
233 } catch (SmbException e) {
234 if (e.getNtStatus() == NT_NOT_SAME_DEVICE) {
235 if (log.isDebugEnabled())
236 log.debug("Failed renaming file '{}' to '{}', falling back to file copy.", from, to);
237 from.copyTo(to);
238 if (log.isDebugEnabled())
239 log.debug("Successfully copied file '{}' to '{}', deleting the source now.", from, to);
240 from.delete();
241 } else
242 throw e;
243 }
244 } else
245 throw new FileNotFoundException(from + " is not a file or does not exist.");
246 } catch (FileNotFoundException e) {
247 log.error("TMACL-01270:Can't rename '" + fromPath + "' to '" + toPath + "'.", e);
248 throw e;
249 } catch (SmbException e) {
250 log.error("TMACL-01280:Can't rename '" + fromPath + "' to '" + toPath + "'.", e);
251 throw createFileNotFound(e);
252 }
253 }
254 }