avoiding stat() races (Was: RE: Samba login)

Cole, Timothy D. timothy_d_cole at md.northgrum.com
Thu Nov 9 21:25:43 GMT 2000


> -----Original Message-----
> From:	Kenichi Okuyama [SMTP:okuyamak at dd.iij4u.or.jp]
> Sent:	Tuesday, November 07, 2000 0:33
> To:	samba-technical at samba.org
> Subject:	Re: Samba login
> 
> JA> For any call that does information retrieval, the stat() call in
> JA> the statcache returns the SMB_STAT_STRUCT into the main SMB
> JA> processing function, and then uses it. 
> 
> Even with SMB*Create*?
> 
	[ note that I speak without having looked at the name-matching code
in Samba for a LONG time, so I may be misunderstanding the scope of the
statcache stuff... general idea still holds, though ]

	The essential problem here is that the verification of the statcache
entry and the intended action really ought to be atomic (as you point out)
-- but to do that, the statcache is going to need to "know" what needs to be
done with the file once it is found.

	Perhaps the statcache API should accept a function pointer (int
(*)(const char *, void *) or similar) and a void * (since we can't do
closures in C -_-)?

	The function passed to the statcache would be used to validate
statcache entries (as well as carry out the intended action atomically),
either returning true to indicate a valid entry, or false to indicate an
invalid one.

	The void * would of course be used by this function as appropriate,
generally holding a pointer to a structure to accept parameters and "return"
values (e.g. open(2) flags/mode, the opened file descriptor, status
information, SMB_STAT_STRUCT, etc).

	I suspect, though, that there are really only two variations that
would be useful, and the latter only as an optimization for very specific
"one-shot" informational deals:

	 typedef int (*dos_name_func)(const char *path, void *param);
	 extern int dos_name_operation(const char *path, dos_name_func func,
void *param);

	 typedef struct __do_open_param {
	   int fd;
	   int flags;
	   int mode;
	 } _do_open_param;
	 static int _do_open(const char *path, _do_open_param *param) {
	   param->fd = open(path, param.flags, param.mode);
	   if ( param->fd == -1 ) {
	     switch (errno) {
	     case ENOENT:
	     case ENOTDIR:
	       return 0;
	     default:
	       return 1;
	     }
	   } else {
	     return 1;
	   }
	 }
	 int dos_name_open(const char *path, int flags, int mode) {
		_do_open_param param;
	   param.flags = flags;  param.mode = mode;
	   if (dos_name_operation(path, (dos_name_func)_do_open, &param)) {
	     return param.fd;
	   } else {
	     errno = ENOENT;
	     return -1;
	   }
	 }

	 typedef struct __do_stat_param {
	   int status;
	   SMB_STAT_STRUCT *st_buf;
	 } _do_stat_param;
	 static int _do_stat(const char *path, _do_stat_param *param) {
	   param->status = stat(path, param->st_buf);
	   if ( param->status == -1 ) {
	     switch (errno) {
	     case ENOENT:
	     case ENOTDIR;
	       return 0;
	     default:
	       return 1;
	     }
	   } else {
	     return 1;
	   }
	 }
	 int dos_name_stat(const char *path, SMB_STAT_STRUCT *st_buf) {
	   _do_stat_param param;
   param.st_buf = st_buf;
   if (dos_name_operation(path, (dos_name_func)_do_stat, &param)) {
     return param.status;
   } else {
     errno = ENOENT;
     return -1;
   }
	 }

	Maybe one for opendir(), too.

	At least, that's how I tend to approach these sorts of things.
Maybe I'm on crack.

	---

	Incidentally, regarding the need you indicated for increased
resolution timestamps -- increasing timestamp resolution would only serve to
"shrink" the window wherein the stat information can be erroneously
identified as still valid.

	Since this would be mucking about with the kernel and filesystem
layout anyway, I think an e.g. 32-bit "generation count" (not in the NFS
sense) on the inode, incremented with every modification would be a
preferable (although still not ideal) solution.




More information about the samba-technical mailing list