Skip to content

Commit

Permalink
handle the O_EXCL flag of open()
Browse files Browse the repository at this point in the history
  • Loading branch information
cesar-douady committed May 1, 2024
1 parent 3e397f3 commit 703f78a
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 26 deletions.
1 change: 1 addition & 0 deletions Manifest
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ unit_tests/misc1.py
unit_tests/misc10.py
unit_tests/misc11.py
unit_tests/misc13.py
unit_tests/misc14.py
unit_tests/misc2.py
unit_tests/misc3.py
unit_tests/misc4.py
Expand Down
46 changes: 22 additions & 24 deletions src/autodep/record.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ using namespace Time ;
//

// if strict, user might look at the st_size field which gives access to regular and link content
static constexpr Accesses UserStatAccess = StrictUserAccesses ? ~Accesses() : Accesses(Access::Stat) ;
static constexpr Accesses UserStatAccesses = StrictUserAccesses ? ~Accesses() : Accesses(Access::Stat) ;

bool Record::s_static_report = false ;
::vmap_s<DepDigest> * Record::s_deps = nullptr ;
Expand Down Expand Up @@ -177,30 +177,28 @@ Record::Mkdir::Mkdir( Record& r , Path&& path , ::string&& c ) : Solve{r,::move(
// However :
// - if it is an official target, it is not a dep, whether you declare reading it or not
// - else, we do not compute a CRC on it and its actual content is not guaranteed. What is important in this case is that the execution of the job does not see the content.
static bool _do_stat (int flags) { return flags&O_PATH ; }
static bool _do_read (int flags) { return !_do_stat(flags) && (flags&O_ACCMODE)!=O_WRONLY && !(flags&O_TRUNC) ; }
static bool _do_write(int flags) { return !_do_stat(flags) && (flags&O_ACCMODE)!=O_RDONLY ; }
Record::Open::Open( Record& r , Path&& path , int flags , ::string&& c ) :
Solve{ r , ::move(path) , bool(flags&O_NOFOLLOW) , _do_read(flags) , true/*allow_tmp_map*/ , to_string(c,::hex,'.',flags) }
, do_write{_do_write(flags)}
{
bool do_read = _do_read(flags) ;
bool do_stat = _do_stat(flags) ;
//
static bool _no_follow(int flags) { return (flags&O_NOFOLLOW) || ( (flags&O_CREAT) && (flags&O_EXCL) ) ; }
static bool _do_stat (int flags) { return flags&O_PATH || ( (flags&O_CREAT) && (flags&O_EXCL) ) ; }
static bool _do_read (int flags) { return !(flags&O_PATH) && (flags&O_ACCMODE)!=O_WRONLY && !(flags&O_TRUNC) ; }
static bool _do_write (int flags) { return !(flags&O_PATH) && (flags&O_ACCMODE)!=O_RDONLY ; }
//
Record::Open::Open( Record& r , Path&& path , int flags , ::string&& c ) : Solve{ r , ::move(path) , _no_follow(flags) , _do_read(flags) , true/*allow_tmp_map*/ , to_string(c,::hex,'.',flags) } {
if ( flags&(O_DIRECTORY|O_TMPFILE) ) return ; // we already solved, this is enough
if ( !(flags&O_PATH) && s_autodep_env().ignore_stat ) return ;
if ( file_loc>FileLoc::Dep ) return ;
//
if ( flags&(O_DIRECTORY|O_TMPFILE) ) { file_loc = FileLoc::Ext ; return ; } // we already solved, this is enough
if ( do_stat && s_autodep_env().ignore_stat ) { file_loc = FileLoc::Ext ; return ; }
if ( file_loc>FileLoc::Dep ) return ;
bool do_stat = _do_stat (flags) ;
bool do_read = _do_read (flags) ;
bool do_write = _do_write(flags) && file_loc==FileLoc::Repo ;
//
if (!do_write) {
if (do_read) r._report_dep ( ::move(real) , accesses|Access::Reg|UserStatAccess , c+".rd" ) ; // user might do fstat on returned fd and if strict user might look at st_size field
else if (do_stat) r._report_dep ( ::move(real) , accesses |UserStatAccess , c+".path" ) ; // .
} else if (file_loc==FileLoc::Repo) {
if (do_read) r._report_update( ::move(real) , accesses|Access::Reg|UserStatAccess , c+".upd" ) ; // file date may be updated if created, user might do a meaningful fstat
else r._report_update( ::move(real) , accesses , c+".wr" ) ; // file date may be updated if created, user cannot see file stat before the write
} else {
if (do_read) r._report_dep ( ::move(real) , accesses|Access::Reg|UserStatAccess , c+".upd" ) ; // in src dirs, only the read side is reported
else r._report_dep ( ::move(real) , accesses , c+".wr" ) ; // .
}
c += '.' ;
if (do_stat ) { c += 'S' ; accesses |= UserStatAccesses ; }
if (do_read ) { c += 'R' ; accesses |= UserStatAccesses|Access::Reg ; }
if (do_write) c += 'W' ;
//
if ( do_write ) { r._report_update( ::move(real) , accesses , ::move(c) ) ; confirm = true ; }
else if ( do_read || do_stat ) r._report_dep ( ::move(real) , accesses , ::move(c) ) ;
}

Record::Read::Read( Record& r , Path&& path , bool no_follow , bool keep_real , bool allow_tmp_map , ::string&& c ) : Solve{r,::move(path),no_follow,true/*read*/,allow_tmp_map,c} {
Expand Down Expand Up @@ -303,7 +301,7 @@ Record::Rename::Rename( Record& r , Path&& src_ , Path&& dst_ , bool exchange ,
}

Record::Stat::Stat( Record& r , Path&& path , bool no_follow , ::string&& c ) : Solve{r,::move(path),no_follow,true/*read*/,true/*allow_tmp_map*/,c} {
if ( !s_autodep_env().ignore_stat && file_loc<=FileLoc::Dep ) r._report_dep( ::move(real) , accesses|UserStatAccess , ::move(c) ) ;
if ( !s_autodep_env().ignore_stat && file_loc<=FileLoc::Dep ) r._report_dep( ::move(real) , accesses|UserStatAccesses , ::move(c) ) ;
}

Record::Symlnk::Symlnk( Record& r , Path&& p , ::string&& c ) : Solve{r,::move(p),true/*no_follow*/,false/*read*/,true/*allow_tmp_map*/,c} {
Expand Down
4 changes: 2 additions & 2 deletions src/autodep/record.hh
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,9 @@ public :
Open() = default ;
Open( Record& , Path&& , int flags , ::string&& comment ) ;
// services
int operator()( Record& r , int rc ) { { if (do_write) r._report_confirm(file_loc,rc>=0) ; } return rc ; }
int operator()( Record& r , int rc ) { { if (confirm) r._report_confirm(file_loc,rc>=0) ; } return rc ; }
// data
bool do_write = false ;
bool confirm = false ;
} ;
struct Read : Solve {
Read() = default ;
Expand Down
36 changes: 36 additions & 0 deletions unit_tests/misc14.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# This file is part of the open-lmake distribution ([email protected]:cesar-douady/open-lmake.git)
# Copyright (c) 2023 Doliam
# This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html).
# This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

if __name__!='__main__' :

import sys
import time

import lmake
from lmake.rules import Rule

lmake.manifest = ('Lmakefile.py',)

class DutSh(Rule) :
targets = { 'DUT' : r'dut_sh{N*:\d}' }
cmd = '''
[ -f dut_sh1 ] && exit 1 ; echo 1 >dut_sh1
[ -f dut_sh2 ] && exit 1 ; echo 2 >dut_sh2
'''

class DutPy(Rule) :
targets = { 'DUT' : r'dut_py{N*:\d}' }
def cmd() :
print(1,file=open('dut_py1','x'))
print(2,file=open('dut_py2','x'))

else :

import ut

print('manual',file=open('dut_sh2','w'))
print('manual',file=open('dut_py2','w'))
ut.lmake( 'dut_sh1' , rerun=1 , done=1 ) # check dut_sh2 is quarantined and job rerun
ut.lmake( 'dut_py1' , rerun=1 , done=1 , new=1 ) # check dut_py2 is quarantined and job rerun

0 comments on commit 703f78a

Please sign in to comment.