Ensure files starting with multiple dots are hidden
[samba.git] / source / smbd / dosmode.c
1 /* 
2    Unix SMB/CIFS implementation.
3    dos mode handling functions
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) James Peach 2006
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22
23 static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
24 {
25 #if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
26         if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
27                 return FILE_ATTRIBUTE_SPARSE;
28         }
29 #endif
30         return 0;
31 }
32
33 /****************************************************************************
34  Change a dos mode to a unix mode.
35     Base permission for files:
36          if creating file and inheriting (i.e. parent_dir != NULL)
37            apply read/write bits from parent directory.
38          else   
39            everybody gets read bit set
40          dos readonly is represented in unix by removing everyone's write bit
41          dos archive is represented in unix by the user's execute bit
42          dos system is represented in unix by the group's execute bit
43          dos hidden is represented in unix by the other's execute bit
44          if !inheriting {
45            Then apply create mask,
46            then add force bits.
47          }
48     Base permission for directories:
49          dos directory is represented in unix by unix's dir bit and the exec bit
50          if !inheriting {
51            Then apply create mask,
52            then add force bits.
53          }
54 ****************************************************************************/
55
56 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
57                  const char *inherit_from_dir)
58 {
59         mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
60         mode_t dir_mode = 0; /* Mode of the inherit_from directory if
61                               * inheriting. */
62
63         if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
64                 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
65         }
66
67         if (fname && (inherit_from_dir != NULL)
68             && lp_inherit_perms(SNUM(conn))) {
69                 SMB_STRUCT_STAT sbuf;
70
71                 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
72                           inherit_from_dir));
73                 if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) {
74                         DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
75                                  inherit_from_dir, strerror(errno)));
76                         return(0);      /* *** shouldn't happen! *** */
77                 }
78
79                 /* Save for later - but explicitly remove setuid bit for safety. */
80                 dir_mode = sbuf.st_mode & ~S_ISUID;
81                 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
82                 /* Clear "result" */
83                 result = 0;
84         } 
85
86         if (IS_DOS_DIR(dosmode)) {
87                 /* We never make directories read only for the owner as under DOS a user
88                 can always create a file in a read-only directory. */
89                 result |= (S_IFDIR | S_IWUSR);
90
91                 if (dir_mode) {
92                         /* Inherit mode of parent directory. */
93                         result |= dir_mode;
94                 } else {
95                         /* Provisionally add all 'x' bits */
96                         result |= (S_IXUSR | S_IXGRP | S_IXOTH);                 
97
98                         /* Apply directory mask */
99                         result &= lp_dir_mask(SNUM(conn));
100                         /* Add in force bits */
101                         result |= lp_force_dir_mode(SNUM(conn));
102                 }
103         } else { 
104                 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
105                         result |= S_IXUSR;
106
107                 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
108                         result |= S_IXGRP;
109  
110                 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
111                         result |= S_IXOTH;  
112
113                 if (dir_mode) {
114                         /* Inherit 666 component of parent directory mode */
115                         result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
116                 } else {
117                         /* Apply mode mask */
118                         result &= lp_create_mask(SNUM(conn));
119                         /* Add in force bits */
120                         result |= lp_force_create_mode(SNUM(conn));
121                 }
122         }
123
124         DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
125         return(result);
126 }
127
128 /****************************************************************************
129  Change a unix mode to a dos mode.
130 ****************************************************************************/
131
132 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
133 {
134         int result = 0;
135         enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
136
137         if (ro_opts == MAP_READONLY_YES) {
138                 /* Original Samba method - map inverse of user "w" bit. */
139                 if ((sbuf->st_mode & S_IWUSR) == 0) {
140                         result |= aRONLY;
141                 }
142         } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
143                 /* Check actual permissions for read-only. */
144                 if (!can_write_to_file(conn, path, sbuf)) {
145                         result |= aRONLY;
146                 }
147         } /* Else never set the readonly bit. */
148
149         if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
150                 result |= aARCH;
151
152         if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
153                 result |= aSYSTEM;
154         
155         if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
156                 result |= aHIDDEN;   
157   
158         if (S_ISDIR(sbuf->st_mode))
159                 result = aDIR | (result & aRONLY);
160
161         result |= set_sparse_flag(sbuf);
162  
163 #ifdef S_ISLNK
164 #if LINKS_READ_ONLY
165         if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
166                 result |= aRONLY;
167 #endif
168 #endif
169
170         DEBUG(8,("dos_mode_from_sbuf returning "));
171
172         if (result & aHIDDEN) DEBUG(8, ("h"));
173         if (result & aRONLY ) DEBUG(8, ("r"));
174         if (result & aSYSTEM) DEBUG(8, ("s"));
175         if (result & aDIR   ) DEBUG(8, ("d"));
176         if (result & aARCH  ) DEBUG(8, ("a"));
177         
178         DEBUG(8,("\n"));
179         return result;
180 }
181
182 /****************************************************************************
183  Get DOS attributes from an EA.
184 ****************************************************************************/
185
186 static bool get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
187 {
188         ssize_t sizeret;
189         fstring attrstr;
190         unsigned int dosattr;
191
192         if (!lp_store_dos_attributes(SNUM(conn))) {
193                 return False;
194         }
195
196         /* Don't reset pattr to zero as we may already have filename-based attributes we
197            need to preserve. */
198
199         sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
200         if (sizeret == -1) {
201                 if (errno == ENOSYS
202 #if defined(ENOTSUP)
203                         || errno == ENOTSUP) {
204 #else
205                                 ) {
206 #endif
207                         DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
208                                 path, strerror(errno) ));
209                         set_store_dos_attributes(SNUM(conn), False);
210                 }
211                 return False;
212         }
213         /* Null terminate string. */
214         attrstr[sizeret] = 0;
215         DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
216
217         if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
218                         sscanf(attrstr, "%x", &dosattr) != 1) {
219                 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
220                 return False;
221         }
222
223         if (S_ISDIR(sbuf->st_mode)) {
224                 dosattr |= aDIR;
225         }
226         *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
227
228         DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
229
230         if (dosattr & aHIDDEN) DEBUG(8, ("h"));
231         if (dosattr & aRONLY ) DEBUG(8, ("r"));
232         if (dosattr & aSYSTEM) DEBUG(8, ("s"));
233         if (dosattr & aDIR   ) DEBUG(8, ("d"));
234         if (dosattr & aARCH  ) DEBUG(8, ("a"));
235         
236         DEBUG(8,("\n"));
237
238         return True;
239 }
240
241 /****************************************************************************
242  Set DOS attributes in an EA.
243 ****************************************************************************/
244
245 static bool set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
246 {
247         fstring attrstr;
248         files_struct *fsp = NULL;
249         bool ret = False;
250
251         if (!lp_store_dos_attributes(SNUM(conn))) {
252                 return False;
253         }
254
255         snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
256         if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
257                 if((errno != EPERM) && (errno != EACCES)) {
258                         if (errno == ENOSYS
259 #if defined(ENOTSUP)
260                                 || errno == ENOTSUP) {
261 #else
262                                 ) {
263 #endif
264                                 DEBUG(1,("set_ea_dos_attributes: Cannot set attribute EA on file %s: Error = %s\n",
265                                         path, strerror(errno) ));
266                                 set_store_dos_attributes(SNUM(conn), False);
267                         }
268                         return False;
269                 }
270
271                 /* We want DOS semantics, ie allow non owner with write permission to change the
272                         bits on a file. Just like file_ntimes below.
273                 */
274
275                 /* Check if we have write access. */
276                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
277                         return False;
278
279                 /*
280                  * We need to open the file with write access whilst
281                  * still in our current user context. This ensures we
282                  * are not violating security in doing the setxattr.
283                  */
284
285                 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp)))
286                         return ret;
287                 become_root();
288                 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
289                         ret = True;
290                 }
291                 unbecome_root();
292                 close_file_fchmod(fsp);
293                 return ret;
294         }
295         DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
296         return True;
297 }
298
299 /****************************************************************************
300  Change a unix mode to a dos mode for an ms dfs link.
301 ****************************************************************************/
302
303 uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
304 {
305         uint32 result = 0;
306
307         DEBUG(8,("dos_mode_msdfs: %s\n", path));
308
309         if (!VALID_STAT(*sbuf)) {
310                 return 0;
311         }
312
313         /* First do any modifications that depend on the path name. */
314         /* hide files with a name starting with a . */
315         if (lp_hide_dot_files(SNUM(conn))) {
316                 const char *p = strrchr_m(path,'/');
317                 if (p) {
318                         p++;
319                 } else {
320                         p = path;
321                 }
322
323                 /* Only . and .. are not hidden. */
324                 if (p[0] == '.' && !((p[1] == '\0') ||
325                                 (p[1] == '.' && p[2] == '\0'))) {
326                         result |= aHIDDEN;
327                 }
328         }
329         
330         result |= dos_mode_from_sbuf(conn, path, sbuf);
331
332         /* Optimization : Only call is_hidden_path if it's not already
333            hidden. */
334         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
335                 result |= aHIDDEN;
336         }
337
338         DEBUG(8,("dos_mode_msdfs returning "));
339
340         if (result & aHIDDEN) DEBUG(8, ("h"));
341         if (result & aRONLY ) DEBUG(8, ("r"));
342         if (result & aSYSTEM) DEBUG(8, ("s"));
343         if (result & aDIR   ) DEBUG(8, ("d"));
344         if (result & aARCH  ) DEBUG(8, ("a"));
345         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
346         
347         DEBUG(8,("\n"));
348
349         return(result);
350 }
351
352 /****************************************************************************
353  Change a unix mode to a dos mode.
354 ****************************************************************************/
355
356 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
357 {
358         uint32 result = 0;
359         bool offline;
360
361         DEBUG(8,("dos_mode: %s\n", path));
362
363         if (!VALID_STAT(*sbuf)) {
364                 return 0;
365         }
366
367         /* First do any modifications that depend on the path name. */
368         /* hide files with a name starting with a . */
369         if (lp_hide_dot_files(SNUM(conn))) {
370                 const char *p = strrchr_m(path,'/');
371                 if (p) {
372                         p++;
373                 } else {
374                         p = path;
375                 }
376
377                 /* Only . and .. are not hidden. */
378                 if (p[0] == '.' && !((p[1] == '\0') ||
379                                 (p[1] == '.' && p[2] == '\0'))) {
380                         result |= aHIDDEN;
381                 }
382         }
383         
384         /* Get the DOS attributes from an EA by preference. */
385         if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
386                 result |= set_sparse_flag(sbuf);
387         } else {
388                 result |= dos_mode_from_sbuf(conn, path, sbuf);
389         }
390
391         
392         offline = SMB_VFS_IS_OFFLINE(conn, path, sbuf);
393         if (S_ISREG(sbuf->st_mode) && offline) {
394                 result |= FILE_ATTRIBUTE_OFFLINE;
395         }
396
397         /* Optimization : Only call is_hidden_path if it's not already
398            hidden. */
399         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
400                 result |= aHIDDEN;
401         }
402
403         DEBUG(8,("dos_mode returning "));
404
405         if (result & aHIDDEN) DEBUG(8, ("h"));
406         if (result & aRONLY ) DEBUG(8, ("r"));
407         if (result & aSYSTEM) DEBUG(8, ("s"));
408         if (result & aDIR   ) DEBUG(8, ("d"));
409         if (result & aARCH  ) DEBUG(8, ("a"));
410         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
411         
412         DEBUG(8,("\n"));
413
414         return(result);
415 }
416
417 /*******************************************************************
418  chmod a file - but preserve some bits.
419 ********************************************************************/
420
421 int file_set_dosmode(connection_struct *conn, const char *fname,
422                      uint32 dosmode, SMB_STRUCT_STAT *st,
423                      const char *parent_dir,
424                      bool newfile)
425 {
426         SMB_STRUCT_STAT st1;
427         int mask=0;
428         mode_t tmp;
429         mode_t unixmode;
430         int ret = -1, lret = -1;
431         uint32_t old_mode;
432
433         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
434         dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
435
436         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
437
438         if (st == NULL) {
439                 SET_STAT_INVALID(st1);
440                 st = &st1;
441         }
442
443         if (!VALID_STAT(*st)) {
444                 if (SMB_VFS_STAT(conn,fname,st))
445                         return(-1);
446         }
447
448         unixmode = st->st_mode;
449
450         get_acl_group_bits(conn, fname, &st->st_mode);
451
452         if (S_ISDIR(st->st_mode))
453                 dosmode |= aDIR;
454         else
455                 dosmode &= ~aDIR;
456
457         old_mode = dos_mode(conn,fname,st);
458         
459         if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
460                 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
461                         lret = SMB_VFS_SET_OFFLINE(conn, fname);
462                         if (lret == -1) {
463                                 DEBUG(0, ("set_dos_mode: client has asked to set "
464                                           "FILE_ATTRIBUTE_OFFLINE to %s/%s but there was "
465                                           "an error while setting it or it is not supported.\n",
466                                           parent_dir, fname));
467                         }
468                 }
469         }
470
471         dosmode  &= ~FILE_ATTRIBUTE_OFFLINE;
472         old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
473
474         if (old_mode == dosmode) {
475                 st->st_mode = unixmode;
476                 return(0);
477         }
478
479         /* Store the DOS attributes in an EA by preference. */
480         if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
481                 if (!newfile) {
482                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
483                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
484                 }
485                 st->st_mode = unixmode;
486                 return 0;
487         }
488
489         unixmode = unix_mode(conn,dosmode,fname, parent_dir);
490
491         /* preserve the s bits */
492         mask |= (S_ISUID | S_ISGID);
493
494         /* preserve the t bit */
495 #ifdef S_ISVTX
496         mask |= S_ISVTX;
497 #endif
498
499         /* possibly preserve the x bits */
500         if (!MAP_ARCHIVE(conn))
501                 mask |= S_IXUSR;
502         if (!MAP_SYSTEM(conn))
503                 mask |= S_IXGRP;
504         if (!MAP_HIDDEN(conn))
505                 mask |= S_IXOTH;
506
507         unixmode |= (st->st_mode & mask);
508
509         /* if we previously had any r bits set then leave them alone */
510         if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
511                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
512                 unixmode |= tmp;
513         }
514
515         /* if we previously had any w bits set then leave them alone 
516                 whilst adding in the new w bits, if the new mode is not rdonly */
517         if (!IS_DOS_READONLY(dosmode)) {
518                 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
519         }
520
521         ret = SMB_VFS_CHMOD(conn, fname, unixmode);
522         if (ret == 0) {
523                 if(!newfile || (lret != -1)) {
524                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
525                                      FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
526                 }
527                 st->st_mode = unixmode;
528                 return 0;
529         }
530
531         if((errno != EPERM) && (errno != EACCES))
532                 return -1;
533
534         if(!lp_dos_filemode(SNUM(conn)))
535                 return -1;
536
537         /* We want DOS semantics, ie allow non owner with write permission to change the
538                 bits on a file. Just like file_ntimes below.
539         */
540
541         /* Check if we have write access. */
542         if (CAN_WRITE(conn)) {
543                 /*
544                  * We need to open the file with write access whilst
545                  * still in our current user context. This ensures we
546                  * are not violating security in doing the fchmod.
547                  * This file open does *not* break any oplocks we are
548                  * holding. We need to review this.... may need to
549                  * break batch oplocks open by others. JRA.
550                  */
551                 files_struct *fsp;
552                 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
553                         return -1;
554                 become_root();
555                 ret = SMB_VFS_FCHMOD(fsp, unixmode);
556                 unbecome_root();
557                 close_file_fchmod(fsp);
558                 if (!newfile) {
559                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
560                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
561                 }
562                 if (ret == 0) {
563                         st->st_mode = unixmode;
564                 }
565         }
566
567         return( ret );
568 }
569
570 /*******************************************************************
571  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
572  than POSIX.
573 *******************************************************************/
574
575 int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
576 {
577         SMB_STRUCT_STAT sbuf;
578         int ret = -1;
579
580         errno = 0;
581         ZERO_STRUCT(sbuf);
582
583         DEBUG(6, ("file_ntime: actime: %s",
584                   time_to_asc(convert_timespec_to_time_t(ts[0]))));
585         DEBUG(6, ("file_ntime: modtime: %s",
586                   time_to_asc(convert_timespec_to_time_t(ts[1]))));
587
588         /* Don't update the time on read-only shares */
589         /* We need this as set_filetime (which can be called on
590            close and other paths) can end up calling this function
591            without the NEED_WRITE protection. Found by : 
592            Leo Weppelman <leo@wau.mis.ah.nl>
593         */
594
595         if (!CAN_WRITE(conn)) {
596                 return 0;
597         }
598
599         if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
600                 return 0;
601         }
602
603         if((errno != EPERM) && (errno != EACCES)) {
604                 return -1;
605         }
606
607         if(!lp_dos_filetimes(SNUM(conn))) {
608                 return -1;
609         }
610
611         /* We have permission (given by the Samba admin) to
612            break POSIX semantics and allow a user to change
613            the time on a file they don't own but can write to
614            (as DOS does).
615          */
616
617         /* Check if we have write access. */
618         if (can_write_to_file(conn, fname, &sbuf)) {
619                 /* We are allowed to become root and change the filetime. */
620                 become_root();
621                 ret = SMB_VFS_NTIMES(conn, fname, ts);
622                 unbecome_root();
623         }
624
625         return ret;
626 }
627
628 /******************************************************************
629  Force a "sticky" write time on a pathname. This will always be
630  returned on all future write time queries and set on close.
631 ******************************************************************/
632
633 bool set_sticky_write_time_path(connection_struct *conn, const char *fname,
634                          struct file_id fileid, const struct timespec mtime)
635 {
636         if (null_timespec(mtime)) {
637                 return true;
638         }
639
640         if (!set_sticky_write_time(fileid, mtime)) {
641                 return false;
642         }
643
644         return true;
645 }
646
647 /******************************************************************
648  Force a "sticky" write time on an fsp. This will always be
649  returned on all future write time queries and set on close.
650 ******************************************************************/
651
652 bool set_sticky_write_time_fsp(struct files_struct *fsp, const struct timespec mtime)
653 {
654         fsp->write_time_forced = true;
655         TALLOC_FREE(fsp->update_write_time_event);
656
657         return set_sticky_write_time_path(fsp->conn, fsp->fsp_name,
658                         fsp->file_id, mtime);
659 }
660
661 /******************************************************************
662  Update a write time immediately, without the 2 second delay.
663 ******************************************************************/
664
665 bool update_write_time(struct files_struct *fsp)
666 {
667         if (!set_write_time(fsp->file_id, timespec_current())) {
668                 return false;
669         }
670
671         notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
672                         FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name);
673
674         return true;
675 }