Fix for 64 bit issues with oplocks and allocation size.
[tprouty/samba.git] / source / smbd / fileio.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    read/write to a files_struct
5    Copyright (C) Andrew Tridgell 1992-1998
6    Copyright (C) Jeremy Allison 2000-2002. - write cache.
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 static BOOL setup_write_cache(files_struct *, SMB_OFF_T);
26
27 /****************************************************************************
28  Seek a file. Try to avoid the seek if possible.
29 ****************************************************************************/
30
31 static SMB_OFF_T seek_file(files_struct *fsp,SMB_OFF_T pos)
32 {
33         SMB_OFF_T seek_ret;
34
35         seek_ret = fsp->conn->vfs_ops.lseek(fsp,fsp->fd,pos,SEEK_SET);
36
37         if(seek_ret == -1) {
38                 DEBUG(0,("seek_file: (%s) sys_lseek failed. Error was %s\n",
39                         fsp->fsp_name, strerror(errno) ));
40                 fsp->pos = -1;
41                 return -1;
42         }
43
44         fsp->pos = seek_ret;
45
46         DEBUG(10,("seek_file (%s): requested pos = %.0f, new pos = %.0f\n",
47                 fsp->fsp_name, (double)pos, (double)fsp->pos ));
48
49         return(fsp->pos);
50 }
51
52 /****************************************************************************
53  Read from write cache if we can.
54 ****************************************************************************/
55
56
57 static BOOL read_from_write_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
58 {
59         write_cache *wcp = fsp->wcp;
60
61         if(!wcp)
62                 return False;
63
64         if(n > wcp->data_size || pos < wcp->offset || pos + n > wcp->offset + wcp->data_size)
65                 return False;
66
67         memcpy(data, wcp->data + (pos - wcp->offset), n);
68
69         DO_PROFILE_INC(writecache_read_hits);
70
71         return True;
72 }
73
74 /****************************************************************************
75  Read from a file.
76 ****************************************************************************/
77
78 ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
79 {
80         ssize_t ret=0,readret;
81
82         /* you can't read from print files */
83         if (fsp->print_file)
84                 return -1;
85
86         /*
87          * Serve from write cache if we can.
88          */
89
90         if(read_from_write_cache(fsp, data, pos, n))
91                 return n;
92
93         flush_write_cache(fsp, READ_FLUSH);
94
95         if (seek_file(fsp,pos) == -1) {
96                 DEBUG(3,("read_file: Failed to seek to %.0f\n",(double)pos));
97                 return(ret);
98         }
99   
100         if (n > 0) {
101 #ifdef DMF_FIX
102                 int numretries = 3;
103 tryagain:
104                 readret = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n);
105                 if (readret == -1) {
106                         if ((errno == EAGAIN) && numretries) {
107                                 DEBUG(3,("read_file EAGAIN retry in 10 seconds\n"));
108                                 (void)sleep(10);
109                                 --numretries;
110                                 goto tryagain;
111                         }
112                         return -1;
113                 }
114 #else /* NO DMF fix. */
115                 readret = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n);
116                 if (readret == -1)
117                         return -1;
118 #endif
119                 if (readret > 0)
120                         ret += readret;
121         }
122
123         DEBUG(10,("read_file (%s): pos = %.0f, size = %lu, returned %lu\n",
124                 fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
125
126         return(ret);
127 }
128
129 /* how many write cache buffers have been allocated */
130 static unsigned int allocated_write_caches;
131
132 /****************************************************************************
133  *Really* write to a file.
134 ****************************************************************************/
135
136 static ssize_t real_write_file(files_struct *fsp,char *data,SMB_OFF_T pos, size_t n)
137 {
138         ssize_t ret;
139
140         if ((pos != -1) && (seek_file(fsp,pos) == -1))
141                 return -1;
142
143         ret = vfs_write_data(fsp,data,n);
144
145         DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n",
146                 fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
147
148         return ret;
149 }
150
151 /****************************************************************************
152 write to a file
153 ****************************************************************************/
154
155 ssize_t write_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n)
156 {
157         write_cache *wcp = fsp->wcp;
158         ssize_t total_written = 0;
159         int write_path = -1; 
160
161         if (fsp->print_file)
162                 return print_job_write(SNUM(fsp->conn), fsp->print_jobid, data, n);
163
164         if (!fsp->can_write) {
165                 errno = EPERM;
166                 return(0);
167         }
168
169         if (!fsp->modified) {
170                 SMB_STRUCT_STAT st;
171                 fsp->modified = True;
172
173                 if (fsp->conn->vfs_ops.fstat(fsp,fsp->fd,&st) == 0) {
174                         int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st);
175                         fsp->size = (SMB_BIG_UINT)st.st_size;
176                         if (MAP_ARCHIVE(fsp->conn) && !IS_DOS_ARCHIVE(dosmode))
177                                 file_chmod(fsp->conn,fsp->fsp_name,dosmode | aARCH,&st);
178
179                         /*
180                          * If this is the first write and we have an exclusive oplock then setup
181                          * the write cache.
182                          */
183
184                         if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) {
185                                 setup_write_cache(fsp, st.st_size);
186                                 wcp = fsp->wcp;
187                         } 
188                 }  
189         }
190
191 #ifdef WITH_PROFILE
192         DO_PROFILE_INC(writecache_total_writes);
193         if (!fsp->oplock_type) {
194                 DO_PROFILE_INC(writecache_non_oplock_writes);
195         }
196 #endif
197
198         /*
199          * If this file is level II oplocked then we need
200          * to grab the shared memory lock and inform all
201          * other files with a level II lock that they need
202          * to flush their read caches. We keep the lock over
203          * the shared memory area whilst doing this.
204          */
205
206         release_level_2_oplocks_on_change(fsp);
207
208 #ifdef WITH_PROFILE
209         if (profile_p && profile_p->writecache_total_writes % 500 == 0) {
210                 DEBUG(3,("WRITECACHE: initwrites=%u abutted=%u total=%u \
211 nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
212                         profile_p->writecache_init_writes,
213                         profile_p->writecache_abutted_writes,
214                         profile_p->writecache_total_writes,
215                         profile_p->writecache_non_oplock_writes,
216                         profile_p->writecache_allocated_write_caches,
217                         profile_p->writecache_num_write_caches,
218                         profile_p->writecache_direct_writes,
219                         profile_p->writecache_num_perfect_writes,
220                         profile_p->writecache_read_hits ));
221
222                 DEBUG(3,("WRITECACHE: Flushes SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d\n",
223                         profile_p->writecache_flushed_writes[SEEK_FLUSH],
224                         profile_p->writecache_flushed_writes[READ_FLUSH],
225                         profile_p->writecache_flushed_writes[WRITE_FLUSH],
226                         profile_p->writecache_flushed_writes[READRAW_FLUSH],
227                         profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH],
228                         profile_p->writecache_flushed_writes[CLOSE_FLUSH],
229                         profile_p->writecache_flushed_writes[SYNC_FLUSH] ));
230         }
231 #endif
232
233         if(!wcp) {
234                 DO_PROFILE_INC(writecache_direct_writes);
235                 total_written = real_write_file(fsp, data, pos, n);
236                 if ((total_written != -1) && (pos + total_written > (SMB_OFF_T)fsp->size))
237                         fsp->size = (SMB_BIG_UINT)(pos + total_written);
238                 return total_written;
239         }
240
241         DEBUG(9,("write_file (%s)(fd=%d pos=%.0f size=%u) wcp->offset=%.0f wcp->data_size=%u\n",
242                 fsp->fsp_name, fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size));
243
244         /* 
245          * If we have active cache and it isn't contiguous then we flush.
246          * NOTE: There is a small problem with running out of disk ....
247          */
248
249         if (wcp->data_size) {
250
251                 BOOL cache_flush_needed = False;
252
253                 if ((pos >= wcp->offset) && (pos <= wcp->offset + wcp->data_size)) {
254       
255                         /* ASCII art.... JRA.
256
257       +--------------+-----
258       | Cached data  | Rest of allocated cache buffer....
259       +--------------+-----
260
261             +-------------------+
262             | Data to write     |
263             +-------------------+
264
265                         */
266
267                         /*
268                          * Start of write overlaps or abutts the existing data.
269                          */
270
271                         size_t data_used = MIN((wcp->alloc_size - (pos - wcp->offset)), n);
272
273                         memcpy(wcp->data + (pos - wcp->offset), data, data_used);
274
275                         /*
276                          * Update the current buffer size with the new data.
277                          */
278
279                         if(pos + data_used > wcp->offset + wcp->data_size)
280                                 wcp->data_size = pos + data_used - wcp->offset;
281
282                         /*
283                          * Update the file size if changed.
284                          */
285
286                         if (wcp->offset + wcp->data_size > wcp->file_size) {
287                                 wcp->file_size = wcp->offset + wcp->data_size;
288                                 fsp->size = (SMB_BIG_UINT)wcp->file_size;
289                         }
290
291                         /*
292                          * If we used all the data then
293                          * return here.
294                          */
295
296                         if(n == data_used)
297                                 return n;
298                         else
299                                 cache_flush_needed = True;
300
301                         /*
302                          * Move the start of data forward by the amount used,
303                          * cut down the amount left by the same amount.
304                          */
305
306                         data += data_used;
307                         pos += data_used;
308                         n -= data_used;
309
310                         DO_PROFILE_INC(writecache_abutted_writes);
311                         total_written = data_used;
312
313                         write_path = 1;
314
315                 } else if ((pos < wcp->offset) && (pos + n > wcp->offset) && 
316                                         (pos + n <= wcp->offset + wcp->alloc_size)) {
317
318                         /* ASCII art.... JRA.
319
320                         +---------------+
321                         | Cache buffer  |
322                         +---------------+
323
324             +-------------------+
325             | Data to write     |
326             +-------------------+
327
328                         */
329
330                         /*
331                          * End of write overlaps the existing data.
332                          */
333
334                         size_t data_used = pos + n - wcp->offset;
335
336                         memcpy(wcp->data, data + n - data_used, data_used);
337
338                         /*
339                          * Update the current buffer size with the new data.
340                          */
341
342                         if(pos + n > wcp->offset + wcp->data_size)
343                                 wcp->data_size = pos + n - wcp->offset;
344
345                         /*
346                          * Update the file size if changed.
347                          */
348
349                         if (wcp->offset + wcp->data_size > wcp->file_size) {
350                                 wcp->file_size = wcp->offset + wcp->data_size;
351                                 fsp->size = (SMB_BIG_UINT)wcp->file_size;
352                         }
353
354                         /*
355                          * We don't need to move the start of data, but we
356                          * cut down the amount left by the amount used.
357                          */
358
359                         n -= data_used;
360
361                         /*
362                          * We cannot have used all the data here.
363                          */
364
365                         cache_flush_needed = True;
366
367                         DO_PROFILE_INC(writecache_abutted_writes);
368                         total_written = data_used;
369
370                         write_path = 2;
371
372                 } else if ( (pos >= wcp->file_size) && 
373                                         (wcp->offset + wcp->data_size == wcp->file_size) &&
374                                         (pos > wcp->offset + wcp->data_size) && 
375                                         (pos < wcp->offset + wcp->alloc_size) ) {
376
377                         /* ASCII art.... JRA.
378
379                        End of file ---->|
380
381                         +---------------+---------------+
382                         | Cached data   | Cache buffer  |
383                         +---------------+---------------+
384
385                                               +-------------------+
386                                               | Data to write     |
387                                               +-------------------+
388
389                         */
390
391                         /*
392                          * Non-contiguous write part of which fits within
393                          * the cache buffer and is extending the file
394                          * and the cache contents reflect the current
395                          * data up to the current end of the file.
396                          */
397
398                         size_t data_used;
399
400                         if(pos + n <= wcp->offset + wcp->alloc_size)
401                                 data_used = n;
402                         else
403                                 data_used = wcp->offset + wcp->alloc_size - pos;
404
405                         /*
406                          * Fill in the non-continuous area with zeros.
407                          */
408
409                         memset(wcp->data + wcp->data_size, '\0',
410                                 pos - (wcp->offset + wcp->data_size) );
411
412                         memcpy(wcp->data + (pos - wcp->offset), data, data_used);
413
414                         /*
415                          * Update the current buffer size with the new data.
416                          */
417
418                         if(pos + data_used > wcp->offset + wcp->data_size)
419                                 wcp->data_size = pos + data_used - wcp->offset;
420
421                         /*
422                          * Update the file size if changed.
423                          */
424
425                         if (wcp->offset + wcp->data_size > wcp->file_size) {
426                                 wcp->file_size = wcp->offset + wcp->data_size;
427                                 fsp->size = (SMB_BIG_UINT)wcp->file_size;
428                         }
429
430                         /*
431                          * If we used all the data then
432                          * return here.
433                          */
434
435                         if(n == data_used)
436                                 return n;
437                         else
438                                 cache_flush_needed = True;
439
440                         /*
441                          * Move the start of data forward by the amount used,
442                          * cut down the amount left by the same amount.
443                          */
444
445                         data += data_used;
446                         pos += data_used;
447                         n -= data_used;
448
449                         DO_PROFILE_INC(writecache_abutted_writes);
450                         total_written = data_used;
451
452                         write_path = 3;
453
454                 } else {
455
456                         /* ASCII art..... JRA.
457
458    Case 1).
459
460                         +---------------+---------------+
461                         | Cached data   | Cache buffer  |
462                         +---------------+---------------+
463
464                                                               +-------------------+
465                                                               | Data to write     |
466                                                               +-------------------+
467
468    Case 2).
469
470                            +---------------+---------------+
471                            | Cached data   | Cache buffer  |
472                            +---------------+---------------+
473
474    +-------------------+
475    | Data to write     |
476    +-------------------+
477
478     Case 3).
479
480                            +---------------+---------------+
481                            | Cached data   | Cache buffer  |
482                            +---------------+---------------+
483
484                   +-----------------------------------------------------+
485                   | Data to write                                       |
486                   +-----------------------------------------------------+
487
488                   */
489
490                         /*
491                          * Write is bigger than buffer, or there is no overlap on the
492                          * low or high ends.
493                          */
494
495                         DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \
496 len = %u\n",fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
497
498                         /*
499                          * Update the file size if needed.
500                          */
501
502                         if(pos + n > wcp->file_size) {
503                                 wcp->file_size = pos + n;
504                                 fsp->size = (SMB_BIG_UINT)wcp->file_size;
505                         }
506
507                         /*
508                          * If write would fit in the cache, and is larger than
509                          * the data already in the cache, flush the cache and
510                          * preferentially copy the data new data into it. Otherwise
511                          * just write the data directly.
512                          */
513
514                         if ( n <= wcp->alloc_size && n > wcp->data_size) {
515                                 cache_flush_needed = True;
516                         } else {
517                                 ssize_t ret = real_write_file(fsp, data, pos, n);
518
519                                 /*
520                                  * If the write overlaps the entire cache, then
521                                  * discard the current contents of the cache.
522                                  * Fix from Rasmus Borup Hansen rbh@math.ku.dk.
523                                  */
524
525                                 if ((pos <= wcp->offset) &&
526                                                 (pos + n >= wcp->offset + wcp->data_size) ) {
527                                         DEBUG(9,("write_file: discarding overwritten write \
528 cache: fd = %d, off=%.0f, size=%u\n", fsp->fd, (double)wcp->offset, (unsigned int)wcp->data_size ));
529                                         wcp->data_size = 0;
530                                 }
531
532                                 DO_PROFILE_INC(writecache_direct_writes);
533                                 if (ret == -1)
534                                         return ret;
535
536                                 if (pos + ret > wcp->file_size) {
537                                         wcp->file_size = pos + ret;
538                                         fsp->size = (SMB_BIG_UINT)wcp->file_size;
539                                 }
540
541                                 return ret;
542                         }
543
544                         write_path = 4;
545
546                 }
547
548                 if(wcp->data_size > wcp->file_size) {
549                         wcp->file_size = wcp->data_size;
550                         fsp->size = (SMB_BIG_UINT)wcp->file_size;
551                 }
552
553                 if (cache_flush_needed) {
554                         DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \
555 n = %u, wcp->offset=%.0f, wcp->data_size=%u\n",
556                                 write_path, fsp->fd, (double)wcp->file_size, (double)pos, (unsigned int)n,
557                                 (double)wcp->offset, (unsigned int)wcp->data_size ));
558
559                         flush_write_cache(fsp, WRITE_FLUSH);
560                 }
561         }
562
563         /*
564          * If the write request is bigger than the cache
565          * size, write it all out.
566          */
567
568         if (n > wcp->alloc_size ) {
569                 ssize_t ret = real_write_file(fsp, data, pos, n);
570                 if (ret == -1)
571                         return -1;
572
573                 if (pos + ret > wcp->file_size) {
574                         wcp->file_size = pos + n;
575                         fsp->size = (SMB_BIG_UINT)wcp->file_size;
576                 }
577
578                 DO_PROFILE_INC(writecache_direct_writes);
579                 return total_written + n;
580         }
581
582         /*
583          * If there's any data left, cache it.
584          */
585
586         if (n) {
587 #ifdef WITH_PROFILE
588                 if (wcp->data_size) {
589                         DO_PROFILE_INC(writecache_abutted_writes);
590                 } else {
591                         DO_PROFILE_INC(writecache_init_writes);
592                 }
593 #endif
594                 memcpy(wcp->data+wcp->data_size, data, n);
595                 if (wcp->data_size == 0) {
596                         wcp->offset = pos;
597                         DO_PROFILE_INC(writecache_num_write_caches);
598                 }
599                 wcp->data_size += n;
600
601                 /*
602                  * Update the file size if changed.
603                  */
604
605                 if (wcp->offset + wcp->data_size > wcp->file_size) {
606                         wcp->file_size = wcp->offset + wcp->data_size;
607                         fsp->size = (SMB_BIG_UINT)wcp->file_size;
608                 }
609                 DEBUG(9,("wcp->offset = %.0f wcp->data_size = %u cache return %u\n",
610                         (double)wcp->offset, (unsigned int)wcp->data_size, (unsigned int)n));
611
612                 total_written += n;
613                 return total_written; /* .... that's a write :) */
614         }
615   
616         return total_written;
617 }
618
619 /****************************************************************************
620  Delete the write cache structure.
621 ****************************************************************************/
622
623 void delete_write_cache(files_struct *fsp)
624 {
625         write_cache *wcp;
626
627         if(!fsp)
628                 return;
629
630         if(!(wcp = fsp->wcp))
631                 return;
632
633         DO_PROFILE_DEC(writecache_allocated_write_caches);
634         allocated_write_caches--;
635
636         SMB_ASSERT(wcp->data_size == 0);
637
638         SAFE_FREE(wcp->data);
639         SAFE_FREE(fsp->wcp);
640
641         DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
642 }
643
644 /****************************************************************************
645  Setup the write cache structure.
646 ****************************************************************************/
647
648 static BOOL setup_write_cache(files_struct *fsp, SMB_OFF_T file_size)
649 {
650         ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn));
651         write_cache *wcp;
652
653         if (allocated_write_caches >= MAX_WRITE_CACHES) 
654                 return False;
655
656         if(alloc_size == 0 || fsp->wcp)
657                 return False;
658
659         if((wcp = (write_cache *)malloc(sizeof(write_cache))) == NULL) {
660                 DEBUG(0,("setup_write_cache: malloc fail.\n"));
661                 return False;
662         }
663
664         wcp->file_size = file_size;
665         wcp->offset = 0;
666         wcp->alloc_size = alloc_size;
667         wcp->data_size = 0;
668         if((wcp->data = malloc(wcp->alloc_size)) == NULL) {
669                 DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n",
670                         (unsigned int)wcp->alloc_size ));
671                 SAFE_FREE(wcp);
672                 return False;
673         }
674
675         memset(wcp->data, '\0', wcp->alloc_size );
676
677         fsp->wcp = wcp;
678         DO_PROFILE_INC(writecache_allocated_write_caches);
679         allocated_write_caches++;
680
681         DEBUG(10,("setup_write_cache: File %s allocated write cache size %u\n",
682                 fsp->fsp_name, wcp->alloc_size ));
683
684         return True;
685 }
686
687 /****************************************************************************
688  Cope with a size change.
689 ****************************************************************************/
690
691 void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size)
692 {
693         fsp->size = (SMB_BIG_UINT)file_size;
694         if(fsp->wcp) {
695                 /* The cache *must* have been flushed before we do this. */
696                 if (fsp->wcp->data_size != 0) {
697                         pstring msg;
698                         slprintf(msg, sizeof(msg)-1, "set_filelen_write_cache: size change \
699 on file %s with write cache size = %u\n", fsp->fsp_name, fsp->wcp->data_size );
700                         smb_panic(msg);
701                 }
702                 fsp->wcp->file_size = file_size;
703         }
704 }
705
706 /*******************************************************************
707  Flush a write cache struct to disk.
708 ********************************************************************/
709
710 ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
711 {
712         write_cache *wcp = fsp->wcp;
713         size_t data_size;
714         ssize_t ret;
715
716         if(!wcp || !wcp->data_size)
717                 return 0;
718
719         data_size = wcp->data_size;
720         wcp->data_size = 0;
721
722         DO_PROFILE_DEC_INC(writecache_num_write_caches,writecache_flushed_writes[reason]);
723
724         DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n",
725                 fsp->fd, (double)wcp->offset, (unsigned int)data_size));
726
727 #ifdef WITH_PROFILE
728         if(data_size == wcp->alloc_size)
729                 DO_PROFILE_INC(writecache_num_perfect_writes);
730 #endif
731
732         ret = real_write_file(fsp, wcp->data, wcp->offset, data_size);
733
734         /*
735          * Ensure file size if kept up to date if write extends file.
736          */
737
738         if ((ret != -1) && (wcp->offset + ret > wcp->file_size))
739                 wcp->file_size = wcp->offset + ret;
740
741         return ret;
742 }
743
744 /*******************************************************************
745 sync a file
746 ********************************************************************/
747
748 void sync_file(connection_struct *conn, files_struct *fsp)
749 {
750         if(lp_strict_sync(SNUM(conn)) && fsp->fd != -1) {
751                 flush_write_cache(fsp, SYNC_FLUSH);
752                 conn->vfs_ops.fsync(fsp,fsp->fd);
753         }
754 }
755
756
757 /************************************************************
758  Perform a stat whether a valid fd or not.
759 ************************************************************/
760
761 int fsp_stat(files_struct *fsp, SMB_STRUCT_STAT *pst)
762 {
763         if (fsp->fd == -1)
764                 return vfs_stat(fsp->conn, fsp->fsp_name, pst);
765         else
766                 return vfs_fstat(fsp,fsp->fd, pst);
767 }