2 Unix SMB/PROXY cache engine.
4 Copyright (C) 2007 Sam Liddicott <sam@liddicott.com>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "libcli/raw/libcliraw.h"
22 #include "libcli/smb_composite/smb_composite.h"
23 #include "auth/auth.h"
24 #include "auth/credentials/credentials.h"
25 #include "ntvfs/ntvfs.h"
26 #include "lib/util/dlinklist.h"
27 #include "param/param.h"
29 #include <sys/types.h>
34 #include "ntvfs/proxy/lib/compression/zlib.h"
37 int mkpath(const char* at
, const char* pathname
, mode_t mode
)
39 char path
[MAXPATHLEN
];
42 struct stat stat_path
;
44 strncpy(path
, at
, MAXPATHLEN
);
47 if (len
==0 || (path
[len
-1]!='/' && len
< MAXPATHLEN
-1)) {
48 strncat(path
,"/",MAXPATHLEN
- len
- 1);
52 strncat(path
, pathname
, MAXPATHLEN
- len
- 1);
54 /* get to the non / portions of pathname */
55 while (*next
=='/') next
++;
56 next
=strchr(next
, '/');
58 while (! next
|| *next
) {
60 if (stat(path
, &stat_path
)) {
61 /* doesn't exist, try to create it */
62 if (errno
!=ENOENT
) return -1;
63 if (mkdir(path
, mode
)) return -1;
64 } else if (! S_ISDIR(stat_path
.st_mode
)) {
65 /* exists, but not a directory */
69 /* so it's a directory (now) */
72 while (*next
=='/') next
++;
73 next
=strchr(next
+1, '/');
78 struct cache_context
*new_cache_context(TALLOC_CTX
* memctx
, const char* root
, const char *server
, const char *share
)
80 struct cache_context
*context
=talloc_zero(memctx
, struct cache_context
);
81 if (! context
) return NULL
;
83 context
->root
=talloc_strdup(context
, root
);
84 context
->server
=strlower_talloc(context
, server
);
85 /* share names are never case sensitive */
86 context
->share
=strlower_talloc(context
, share
);
87 context
->prefix
=talloc_asprintf(context
,"%s/%s/", context
->server
, context
->share
);
88 DEBUG(5,("New cache context: root=%s, prefix=%s\n",
89 context
->root
, context
->prefix
));
93 void clear_cache_storage(struct cache_file_entry
*cache
)
95 // if (cache->store_name && *(cache->store_name)) {
96 // unlink(cache->store_name);
97 // talloc_free(cache->store_name);
98 // cache->store_name=NULL;
102 /* op-lock has probably been broken. We can no-longer read-cache for this file
103 in fact the whole cache is invalid for this file now, but we can use it as
104 a reference for delta-compression syncs if the remote server supports it */
105 void cache_file_stale(struct cache_file_entry
*cache
)
108 cache
->status
&= ~ CACHE_READ
;
109 cache
->validated_extent
=0;
110 DEBUG(CACHE_DEBUG_LEVEL
,("Cache file %s no longer good to read from\n",cache
->cache_name
));
114 void cache_close(struct cache_file_entry
*cache
) {
115 if (cache
&& cache
->fd
>=0) {
121 void cache_beopen(struct cache_file_entry
*cache
) {
122 if (cache
->fd
<=0) cache_reopen(cache
);
125 void cache_reopen(struct cache_file_entry
*cache
) {
127 cache
->fd
=open(cache
->pathname
,O_RDWR
| O_CREAT
,S_IRWXU
);
130 void cache_create(struct cache_file_entry
*cache
, int readahead_window
)
132 if (cache
->status
!= CACHE_NONE
&& cache
->fd
<0) {
133 mkpath(cache
->context
->root
, cache
->context
->prefix
, S_IRWXU
);
134 cache
->pathname
=talloc_asprintf(cache
, "%s/%s",
135 cache
->context
->root
,
139 cache
->readahead_window
=readahead_window
;
142 void cache_new_file(struct cache_file_entry
* cache
, struct proxy_file
*f
,
143 const char* filename
,
144 int readahead_window
, bool oplock
)
148 cache
->status
=CACHE_FILL
| CACHE_READ_AHEAD
;
149 DEBUG(5,("NEW CACHE FILE %x oplock %x\n",cache
->status
,oplock
));
150 if (oplock
) cache
->status
|=CACHE_READ
| CACHE_VALIDATE
;
151 cache_create(cache
, readahead_window
);
154 void cache_file_state(struct cache_file_entry
* cache
, cache_state state
)
159 static ssize_t
flen(int fd
)
163 if (fstat(fd
, &stats
)==0) {
164 return (ssize_t
)stats
.st_size
;
169 ssize_t
cache_len(struct cache_file_entry
*cache
)
171 return flen(cache
->fd
);
174 void cache_validated(struct cache_file_entry
*cache
, off_t offset
)
176 if (cache
->validated_extent
<= offset
) {
177 cache
->validated_extent
=offset
;
179 if (cache
->readahead_extent
< offset
) {
180 cache
->readahead_extent
=offset
;
184 void cache_save(struct cache_file_entry
*cache
, const uint8_t* data
, ssize_t size
, off_t offset
)
186 if (cache
&& size
> 0 && cache
->status
& CACHE_FILL
) {
189 if (cache
->fd
< 0) return;
191 DEBUG(1,("Consider saving in cache\n"));
192 /* until we manage sparse caching, the offset must be within, or adjacent
193 * to what has been cached, without an intervening gap - as we have no way
194 * of encoding that the gap is not valid cache */
196 if (offset
<= len
+1 && lseek(cache
->fd
, offset
, SEEK_SET
)>=0) {
197 /* save cache value */
198 written
=write(cache
->fd
, data
, size
);
199 /* only extend the validated extent if the cache is good for reading from */
200 if (cache
->status
& CACHE_READ
&& cache
->validated_extent
< (offset
+ size
)
201 /* but don't extend if it would leave unvalidated gaps */
202 && cache
->validated_extent
>= offset
)
203 cache
->validated_extent
=offset
+ size
;
204 DEBUG(CACHE_DEBUG_LEVEL
,("Save cache written at %d %d bytes\n",(int)offset
,(int)written
));
206 if (cache
->readahead_extent
< offset
+size
) {
207 cache
->readahead_extent
=offset
+size
;
209 } else DEBUG(CACHE_DEBUG_LEVEL
,("No sparse cache support, ignoring write at %d which is beyond %d\n",(int)offset
,(int)len
));
210 } else DEBUG(CACHE_DEBUG_LEVEL
,("No caching on file\n"));
213 ssize_t
cache_raw_read(struct cache_file_entry
*cache
, uint8_t* data
, off_t offset
, ssize_t size
)
215 return pread(cache
->fd
, data
, size
, offset
);
218 /* This function should track changes in rawreadwrite.c/smb_raw_read_send */
219 NTSTATUS
cache_smb_raw_read(struct cache_file_entry
*cache
,
220 struct ntvfs_module_context
*ntvfs
,
221 struct ntvfs_request
*req
,
225 /* Based on pvfs_read.c/pvfs-read */
231 if (validated
) *validated
=0;
233 DEBUG(CACHE_DEBUG_LEVEL
,("Actually trying to read from the cache len %d offset %d\n",(int)rd
->generic
.in
.maxcnt
,(int)rd
->generic
.in
.offset
));
234 /* Can we actually read ahead on this file anyway? */
236 DEBUG(CACHE_DEBUG_LEVEL
,("Not a valid cache\n"));
237 return NT_STATUS_UNSUCCESSFUL
;
241 return NT_STATUS_INVALID_HANDLE
;
244 if (! (cache
->status
& CACHE_READ
) ) {
245 DEBUG(CACHE_DEBUG_LEVEL
,("Cache %s not in read mode: %x\n",cache
->cache_name
, cache
->status
));
246 return NT_STATUS_UNSUCCESSFUL
;
249 /* mapping should have been done by caller */
250 if (rd
->generic
.level
!= RAW_READ_READX
) {
251 return NT_STATUS_INVALID_LEVEL
;
254 mask
= SEC_FILE_READ_DATA
;
255 if (rd
->generic
.in
.read_for_execute
) {
256 mask
|= SEC_FILE_EXECUTE
;
258 #warning need to check access mask
259 /* if (!(f->access_mask & mask)) {
260 return NT_STATUS_ACCESS_DENIED;
263 maxcnt
= rd
->generic
.in
.maxcnt
;
264 if (maxcnt
> UINT16_MAX
&& req
->ctx
->protocol
< PROTOCOL_SMB2
) {
269 /* if read would return 0 bytes then we must fail unless we know the cache is fully populated */
270 if (/*! fully-populated && */rd
->generic
.in
.offset
>= len
) {
271 DEBUG(CACHE_DEBUG_LEVEL
,("Can't read %d bytes from position %d which is %d beyond cache length %d\n",(int)maxcnt
,(int)rd
->generic
.in
.offset
,(int)(maxcnt
+ rd
->generic
.in
.offset
- len
),(int)len
));
272 return NT_STATUS_UNSUCCESSFUL
;
275 #warning need to check locks
276 /* er... I guess we need to be spying on locks, or something */
278 status = pvfs_check_lock(pvfs, f, req->smbpid,
279 rd->generic.in.offset,
282 if (!NT_STATUS_IS_OK(status)) {
287 ret
= pread(cache
->fd
,
288 rd
->generic
.out
.data
,
290 rd
->generic
.in
.offset
);
294 /* Why do I assume it is running on a unix? */
295 DEBUG(CACHE_DEBUG_LEVEL
,("Error %x Read %d bytes from cache offset %d\n",e
,ret
,(int)rd
->generic
.in
.offset
));
296 return map_nt_error_from_unix(e
);
299 /* Does the cache need to do handle position tracking? */
300 /* f->handle->position = f->handle->seek_offset = rd->generic.in.offset + ret; */
302 rd
->generic
.out
.nread
= ret
;
303 rd
->generic
.out
.remaining
= 0xFFFF;
304 rd
->generic
.out
.compaction_mode
= 0;
306 /* How much of what we have is validated cache? */
307 if (! validated
); /* just yawn */
308 else if (cache
->validated_extent
< rd
->generic
.in
.offset
)
310 else if (cache
->validated_extent
>= rd
->generic
.in
.offset
+
311 rd
->generic
.out
.nread
)
312 *validated
=rd
->generic
.out
.nread
;
313 else *validated
=cache
->validated_extent
- rd
->generic
.in
.offset
;
315 DEBUG(CACHE_DEBUG_LEVEL
,("Cache successfully read %d bytes validated %d\n",(int)ret
, (int)((validated
)?(*validated
):-1) ));
320 /* Take MD5 checksum of part of cache file */
321 NTSTATUS
cache_smb_raw_checksum(struct cache_file_entry
*cache
,
322 offset_t offset
, ssize_t
* length
, uint8_t digest
[16])
324 struct MD5Context context
;
327 ssize_t remaining
=*length
;
328 #define buffer_size 4096
329 uint8_t buffer
[buffer_size
];
334 ret
= pread(cache
->fd
, buffer
, MIN(buffer_size
, remaining
), offset
);
339 MD5Update(&context
, buffer
, ret
);
342 MD5Final(digest
, &context
);
345 if (length
==0) return NT_STATUS_UNSUCCESSFUL
;
349 /* Set up caching. We want to pass a reference to the file we opened as
350 * part of the cache key. We see this note in interfaces.h:
351 NOTE: fname can also be a pointer to a
352 uint64_t file_id if create_options has the
353 NTCREATEX_OPTIONS_OPEN_BY_FILE_ID flag set
354 * which is nice! We need to notice! */
356 struct cache_file_entry
* cache_filename_open(struct cache_context
*cache_context
,
357 struct proxy_file
*f
,
358 const char* filename
,
360 int readahead_window
)
362 struct cache_file_entry
*cache
=talloc_zero(f
, struct cache_file_entry
);
365 cache
->context
=talloc_reference(cache
, cache_context
);
366 cache
->cache_name
=talloc_asprintf(cache
,"%s/cache-%d-f-%s",
367 cache
->context
->prefix
,0,filename
);
368 DEBUG(1,("Session %d open proxied for file: %s, cache %s RA %d\n",
369 0, filename
, cache
->cache_name
, readahead_window
));
371 cache_new_file(cache
, f
, cache
->cache_name
, readahead_window
, oplock
);
376 struct cache_file_entry
* cache_fileid_open(struct cache_context
*cache_context
,
377 struct proxy_file
*f
,
380 int readahead_window
)
383 struct cache_file_entry
*cache
=talloc_zero(f
, struct cache_file_entry
);
386 cache
->context
=talloc_reference(cache
, cache_context
);
387 cache
->cache_name
=talloc_asprintf(cache
,"%s/cache-%d-id-%lld",
388 cache
->context
->prefix
,0,*id
);
389 DEBUG(1,("Session %d open proxied for file-id: %lld, cache %s RA %d\n",
390 0, *id
,cache
->cache_name
, readahead_window
));
392 cache_new_file(cache
, f
, cache
->cache_name
, readahead_window
, oplock
);