diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2014-05-09 15:33:00 -0400 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2014-05-09 15:33:00 -0400 |
commit | 1a3ee71de258b416ca6b80f0a9e3b91460df8dc7 (patch) | |
tree | a59167e50add55b51ed31c65944cfa78ddd1c290 | |
parent | f3dda65de157f2d7c35286018c20cbc7597ed748 (diff) | |
parent | eae498c4c5d34c1d0af40ecb430cbbc23b0a8e97 (diff) | |
download | rneovim-1a3ee71de258b416ca6b80f0a9e3b91460df8dc7.tar.gz rneovim-1a3ee71de258b416ca6b80f0a9e3b91460df8dc7.tar.bz2 rneovim-1a3ee71de258b416ca6b80f0a9e3b91460df8dc7.zip |
Merge pull request #619 from stefan991/mch_stat-cleanup
Replace `struct stat` with `FileInfo`
-rw-r--r-- | src/buffer.c | 172 | ||||
-rw-r--r-- | src/buffer_defs.h | 6 | ||||
-rw-r--r-- | src/diff.c | 6 | ||||
-rw-r--r-- | src/eval.c | 69 | ||||
-rw-r--r-- | src/ex_cmds.c | 35 | ||||
-rw-r--r-- | src/ex_cmds2.c | 45 | ||||
-rw-r--r-- | src/file_search.c | 47 | ||||
-rw-r--r-- | src/fileio.c | 259 | ||||
-rw-r--r-- | src/fileio.h | 3 | ||||
-rw-r--r-- | src/if_cscope.c | 91 | ||||
-rw-r--r-- | src/if_cscope_defs.h | 5 | ||||
-rw-r--r-- | src/macros.h | 15 | ||||
-rw-r--r-- | src/main.c | 13 | ||||
-rw-r--r-- | src/memfile.c | 48 | ||||
-rw-r--r-- | src/memline.c | 92 | ||||
-rw-r--r-- | src/misc2.c | 18 | ||||
-rw-r--r-- | src/os/fs.c | 48 | ||||
-rw-r--r-- | src/os/os.h | 38 | ||||
-rw-r--r-- | src/os_unix_defs.h | 2 | ||||
-rw-r--r-- | src/path.c | 30 | ||||
-rw-r--r-- | src/quickfix.c | 14 | ||||
-rw-r--r-- | src/spell.c | 5 | ||||
-rw-r--r-- | src/tag.c | 8 | ||||
-rw-r--r-- | src/undo.c | 45 | ||||
-rw-r--r-- | test/unit/os/fs.moon | 105 |
25 files changed, 578 insertions, 641 deletions
diff --git a/src/buffer.c b/src/buffer.c index d2379f4afa..7cd773b55c 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -65,6 +65,10 @@ #include "ui.h" #include "undo.h" #include "window.h" +#include "os/os.h" + +// Todo(stefan991): remove this macro +#define INVALID_DEVICE_ID UINT64_MAX static char_u *buflist_match(regprog_T *prog, buf_T *buf); # define HAVE_BUFLIST_MATCH @@ -72,25 +76,15 @@ static char_u *fname_match(regprog_T *prog, char_u *name); static void buflist_setfpos(buf_T *buf, win_T *win, linenr_T lnum, colnr_T col, int copy_options); static wininfo_T *find_wininfo(buf_T *buf, int skip_diff_buffer); -#ifdef UNIX -static buf_T *buflist_findname_stat(char_u *ffname, struct stat *st); -static int otherfile_buf(buf_T *buf, char_u *ffname, struct stat *stp); -static int buf_same_ino(buf_T *buf, struct stat *stp); -#else -static int otherfile_buf(buf_T *buf, char_u *ffname); -#endif +static buf_T *buflist_findname_file_info(char_u *ffname, FileInfo *file_info); +static int otherfile_buf(buf_T *buf, char_u *ffname, FileInfo *file_info); +static int buf_same_ino(buf_T *buf, FileInfo *file_info); static int ti_change(char_u *str, char_u **last); static int append_arg_number(win_T *wp, char_u *buf, int buflen, int add_file); static void free_buffer(buf_T *); static void free_buffer_stuff(buf_T *buf, int free_options); static void clear_wininfo(buf_T *buf); -#ifdef UNIX -# define dev_T dev_t -#else -# define dev_T unsigned -#endif - static void insert_sign(buf_T *buf, signlist_T *prev, signlist_T *next, int id, linenr_T lnum, int typenr); static char *msg_loclist = N_("[Location List]"); @@ -1304,29 +1298,20 @@ buflist_new ( ) { buf_T *buf; -#ifdef UNIX - struct stat st; -#endif fname_expand(curbuf, &ffname, &sfname); /* will allocate ffname */ /* * If file name already exists in the list, update the entry. */ -#ifdef UNIX - /* On Unix we can use inode numbers when the file exists. Works better + /* We can use inode numbers when the file exists. Works better * for hard links. */ - if (sfname == NULL || mch_stat((char *)sfname, &st) < 0) - st.st_dev = (dev_T)-1; -#endif - if (ffname != NULL && !(flags & BLN_DUMMY) && (buf = -#ifdef UNIX - buflist_findname_stat(ffname, - &st) -#else - buflist_findname(ffname) -#endif - ) != NULL) { + FileInfo file_info; + if (sfname == NULL || !os_get_file_info((char *)sfname, &file_info)) { + file_info.stat.st_dev = INVALID_DEVICE_ID; + } + if (ffname != NULL && !(flags & BLN_DUMMY) + && (buf = buflist_findname_file_info(ffname, &file_info)) != NULL) { free(ffname); if (lnum != 0) buflist_setfpos(buf, curwin, lnum, (colnr_T)0, FALSE); @@ -1452,15 +1437,13 @@ buflist_new ( hash_init(&buf->b_s.b_keywtab_ic); buf->b_fname = buf->b_sfname; -#ifdef UNIX - if (st.st_dev == (dev_T)-1) + if (file_info.stat.st_dev == INVALID_DEVICE_ID) buf->b_dev_valid = FALSE; else { buf->b_dev_valid = TRUE; - buf->b_dev = st.st_dev; - buf->b_ino = st.st_ino; + buf->b_dev = file_info.stat.st_dev; + buf->b_ino = file_info.stat.st_ino; } -#endif buf->b_u_synced = TRUE; buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED; if (flags & BLN_DUMMY) @@ -1682,31 +1665,28 @@ buf_T *buflist_findname_exp(char_u *fname) */ buf_T *buflist_findname(char_u *ffname) { -#ifdef UNIX - struct stat st; - - if (mch_stat((char *)ffname, &st) < 0) - st.st_dev = (dev_T)-1; - return buflist_findname_stat(ffname, &st); + FileInfo file_info; + if (!os_get_file_info((char *)ffname, &file_info)) { + file_info.stat.st_dev = INVALID_DEVICE_ID; + } + return buflist_findname_file_info(ffname, &file_info); } /* - * Same as buflist_findname(), but pass the stat structure to avoid getting it - * twice for the same file. + * Same as buflist_findname(), but pass the FileInfo structure to avoid + * getting it twice for the same file. * Returns NULL if not found. */ -static buf_T *buflist_findname_stat(char_u *ffname, struct stat *stp) +static buf_T *buflist_findname_file_info(char_u *ffname, FileInfo *file_info) { -#endif buf_T *buf; - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - if ((buf->b_flags & BF_DUMMY) == 0 && !otherfile_buf(buf, ffname -#ifdef UNIX - , stp -#endif - )) + for (buf = firstbuf; buf != NULL; buf = buf->b_next) { + if ((buf->b_flags & BF_DUMMY) == 0 + && !otherfile_buf(buf, ffname, file_info)) { return buf; + } + } return NULL; } @@ -2220,9 +2200,7 @@ setfname ( ) { buf_T *obuf = NULL; -#ifdef UNIX - struct stat st; -#endif + FileInfo file_info; if (ffname == NULL || *ffname == NUL) { /* Removing the name. */ @@ -2230,9 +2208,7 @@ setfname ( free(buf->b_sfname); buf->b_ffname = NULL; buf->b_sfname = NULL; -#ifdef UNIX - st.st_dev = (dev_T)-1; -#endif + file_info.stat.st_dev = INVALID_DEVICE_ID; } else { fname_expand(buf, &ffname, &sfname); /* will allocate ffname */ if (ffname == NULL) /* out of memory */ @@ -2243,16 +2219,12 @@ setfname ( * - if the buffer is loaded, fail * - if the buffer is not loaded, delete it from the list */ -#ifdef UNIX - if (mch_stat((char *)ffname, &st) < 0) - st.st_dev = (dev_T)-1; -#endif - if (!(buf->b_flags & BF_DUMMY)) -#ifdef UNIX - obuf = buflist_findname_stat(ffname, &st); -#else - obuf = buflist_findname(ffname); -#endif + if (!os_get_file_info((char *)ffname, &file_info)) { + file_info.stat.st_dev = INVALID_DEVICE_ID; + } + if (!(buf->b_flags & BF_DUMMY)) { + obuf = buflist_findname_file_info(ffname, &file_info); + } if (obuf != NULL && obuf != buf) { if (obuf->b_ml.ml_mfp != NULL) { /* it's loaded, fail */ if (message) @@ -2278,15 +2250,13 @@ setfname ( buf->b_sfname = sfname; } buf->b_fname = buf->b_sfname; -#ifdef UNIX - if (st.st_dev == (dev_T)-1) + if (file_info.stat.st_dev == INVALID_DEVICE_ID) { buf->b_dev_valid = FALSE; - else { + } else { buf->b_dev_valid = TRUE; - buf->b_dev = st.st_dev; - buf->b_ino = st.st_ino; + buf->b_dev = file_info.stat.st_dev; + buf->b_ino = file_info.stat.st_ino; } -#endif buf_name_changed(buf); return OK; @@ -2419,80 +2389,72 @@ void buflist_altfpos(win_T *win) */ int otherfile(char_u *ffname) { - return otherfile_buf(curbuf, ffname -#ifdef UNIX - , NULL -#endif - ); + return otherfile_buf(curbuf, ffname, NULL); } -static int otherfile_buf(buf_T *buf, char_u *ffname -#ifdef UNIX - , struct stat *stp -#endif -) +static int otherfile_buf(buf_T *buf, char_u *ffname, FileInfo *file_info_p) { /* no name is different */ - if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL) + if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL) { return TRUE; - if (fnamecmp(ffname, buf->b_ffname) == 0) + } + if (fnamecmp(ffname, buf->b_ffname) == 0) { return FALSE; -#ifdef UNIX + } { - struct stat st; + FileInfo file_info; /* If no struct stat given, get it now */ - if (stp == NULL) { - if (!buf->b_dev_valid || mch_stat((char *)ffname, &st) < 0) - st.st_dev = (dev_T)-1; - stp = &st; + if (file_info_p == NULL) { + if (!buf->b_dev_valid || !os_get_file_info((char *)ffname, &file_info)) { + file_info.stat.st_dev = INVALID_DEVICE_ID; + } + file_info_p = &file_info; } /* Use dev/ino to check if the files are the same, even when the names * are different (possible with links). Still need to compare the * name above, for when the file doesn't exist yet. * Problem: The dev/ino changes when a file is deleted (and created * again) and remains the same when renamed/moved. We don't want to - * mch_stat() each buffer each time, that would be too slow. Get the + * stat() each buffer each time, that would be too slow. Get the * dev/ino again when they appear to match, but not when they appear * to be different: Could skip a buffer when it's actually the same * file. */ - if (buf_same_ino(buf, stp)) { + if (buf_same_ino(buf, file_info_p)) { buf_setino(buf); - if (buf_same_ino(buf, stp)) + if (buf_same_ino(buf, file_info_p)) return FALSE; } } -#endif return TRUE; } -#if defined(UNIX) || defined(PROTO) /* * Set inode and device number for a buffer. * Must always be called when b_fname is changed!. */ void buf_setino(buf_T *buf) { - struct stat st; - - if (buf->b_fname != NULL && mch_stat((char *)buf->b_fname, &st) >= 0) { + FileInfo file_info; + if (buf->b_fname != NULL + && os_get_file_info((char *)buf->b_fname, &file_info)) { buf->b_dev_valid = TRUE; - buf->b_dev = st.st_dev; - buf->b_ino = st.st_ino; - } else + buf->b_dev = file_info.stat.st_dev; + buf->b_ino = file_info.stat.st_ino; + } else { buf->b_dev_valid = FALSE; + } } /* * Return TRUE if dev/ino in buffer "buf" matches with "stp". */ -static int buf_same_ino(buf_T *buf, struct stat *stp) +static int buf_same_ino(buf_T *buf, FileInfo *file_info) { return buf->b_dev_valid - && stp->st_dev == buf->b_dev - && stp->st_ino == buf->b_ino; + && file_info->stat.st_dev == buf->b_dev + && file_info->stat.st_ino == buf->b_ino; } -#endif /* * Print info about the current buffer. diff --git a/src/buffer_defs.h b/src/buffer_defs.h index 5c55173022..d78cf0651f 100644 --- a/src/buffer_defs.h +++ b/src/buffer_defs.h @@ -423,11 +423,9 @@ struct file_buffer { char_u *b_sfname; /* short file name */ char_u *b_fname; /* current file name */ -#ifdef UNIX int b_dev_valid; /* TRUE when b_dev has a valid number */ - dev_t b_dev; /* device number */ - ino_t b_ino; /* inode number */ -#endif + uint64_t b_dev; /* device number */ + uint64_t b_ino; /* inode number */ int b_fnum; /* buffer number for this file. */ diff --git a/src/diff.c b/src/diff.c index a93f1324fe..e360edc665 100644 --- a/src/diff.c +++ b/src/diff.c @@ -863,8 +863,6 @@ void ex_diffpatch(exarg_T *eap) char_u dirbuf[MAXPATHL]; char_u *fullname = NULL; #endif // ifdef UNIX - struct stat st; - // We need two temp file names. // Name of original temp file. char_u *tmp_orig = vim_tempname('o'); @@ -965,7 +963,9 @@ void ex_diffpatch(exarg_T *eap) os_remove((char *)buf); // Only continue if the output file was created. - if ((mch_stat((char *)tmp_new, &st) < 0) || (st.st_size == 0)) { + off_t file_size; + bool file_size_success = os_get_file_size((char *)tmp_new, &file_size); + if (!file_size_success || file_size == 0) { EMSG(_("E816: Cannot read patch output")); } else { if (curbuf->b_fname != NULL) { diff --git a/src/eval.c b/src/eval.c index ed246c363b..9300b9a77b 100644 --- a/src/eval.c +++ b/src/eval.c @@ -9664,24 +9664,21 @@ static void f_getfontname(typval_T *argvars, typval_T *rettv) */ static void f_getfperm(typval_T *argvars, typval_T *rettv) { - char_u *fname; - struct stat st; - char_u *perm = NULL; + char_u *perm = NULL; char_u flags[] = "rwx"; - int i; - fname = get_tv_string(&argvars[0]); - - rettv->v_type = VAR_STRING; - if (mch_stat((char *)fname, &st) >= 0) { + char_u *filename = get_tv_string(&argvars[0]); + int32_t file_perm = os_getperm(filename); + if (file_perm >= 0) { perm = vim_strsave((char_u *)"---------"); if (perm != NULL) { - for (i = 0; i < 9; i++) { - if (st.st_mode & (1 << (8 - i))) + for (int i = 0; i < 9; i++) { + if (file_perm & (1 << (8 - i))) perm[i] = flags[i % 3]; } } } + rettv->v_type = VAR_STRING; rettv->vval.v_string = perm; } @@ -9690,25 +9687,25 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv) */ static void f_getfsize(typval_T *argvars, typval_T *rettv) { - char_u *fname; - struct stat st; - - fname = get_tv_string(&argvars[0]); + char *fname = (char *)get_tv_string(&argvars[0]); rettv->v_type = VAR_NUMBER; - if (mch_stat((char *)fname, &st) >= 0) { - if (os_isdir(fname)) + off_t file_size; + if (os_get_file_size(fname, &file_size)) { + if (os_isdir((char_u *)fname)) rettv->vval.v_number = 0; else { - rettv->vval.v_number = (varnumber_T)st.st_size; + rettv->vval.v_number = (varnumber_T)file_size; /* non-perfect check for overflow */ - if ((off_t)rettv->vval.v_number != (off_t)st.st_size) + if ((off_t)rettv->vval.v_number != file_size) { rettv->vval.v_number = -2; + } } - } else + } else { rettv->vval.v_number = -1; + } } /* @@ -9716,15 +9713,14 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv) */ static void f_getftime(typval_T *argvars, typval_T *rettv) { - char_u *fname; - struct stat st; + char *fname = (char *)get_tv_string(&argvars[0]); - fname = get_tv_string(&argvars[0]); - - if (mch_stat((char *)fname, &st) >= 0) - rettv->vval.v_number = (varnumber_T)st.st_mtime; - else + FileInfo file_info; + if (os_get_file_info(fname, &file_info)) { + rettv->vval.v_number = (varnumber_T)file_info.stat.st_mtim.tv_sec; + } else { rettv->vval.v_number = -1; + } } /* @@ -9733,44 +9729,45 @@ static void f_getftime(typval_T *argvars, typval_T *rettv) static void f_getftype(typval_T *argvars, typval_T *rettv) { char_u *fname; - struct stat st; char_u *type = NULL; char *t; fname = get_tv_string(&argvars[0]); rettv->v_type = VAR_STRING; - if (mch_lstat((char *)fname, &st) >= 0) { + FileInfo file_info; + if (os_get_file_info_link((char *)fname, &file_info)) { + uint64_t mode = file_info.stat.st_mode; #ifdef S_ISREG - if (S_ISREG(st.st_mode)) + if (S_ISREG(mode)) t = "file"; - else if (S_ISDIR(st.st_mode)) + else if (S_ISDIR(mode)) t = "dir"; # ifdef S_ISLNK - else if (S_ISLNK(st.st_mode)) + else if (S_ISLNK(mode)) t = "link"; # endif # ifdef S_ISBLK - else if (S_ISBLK(st.st_mode)) + else if (S_ISBLK(mode)) t = "bdev"; # endif # ifdef S_ISCHR - else if (S_ISCHR(st.st_mode)) + else if (S_ISCHR(mode)) t = "cdev"; # endif # ifdef S_ISFIFO - else if (S_ISFIFO(st.st_mode)) + else if (S_ISFIFO(mode)) t = "fifo"; # endif # ifdef S_ISSOCK - else if (S_ISSOCK(st.st_mode)) + else if (S_ISSOCK(mode)) t = "fifo"; # endif else t = "other"; #else # ifdef S_IFMT - switch (st.st_mode & S_IFMT) { + switch (mode & S_IFMT) { case S_IFREG: t = "file"; break; case S_IFDIR: t = "dir"; break; # ifdef S_IFLNK diff --git a/src/ex_cmds.c b/src/ex_cmds.c index eb42acd93f..3613a02cb3 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -1492,14 +1492,10 @@ void write_viminfo(char_u *file, int forceit) FILE *fp_in = NULL; /* input viminfo file, if any */ FILE *fp_out = NULL; /* output viminfo file */ char_u *tempname = NULL; /* name of temp viminfo file */ - struct stat st_new; /* mch_stat() of potential new file */ char_u *wp; #if defined(UNIX) mode_t umask_save; #endif -#ifdef UNIX - struct stat st_old; /* mch_stat() of existing viminfo file */ -#endif if (no_viminfo()) return; @@ -1511,7 +1507,7 @@ void write_viminfo(char_u *file, int forceit) fp_in = mch_fopen((char *)fname, READBIN); if (fp_in == NULL) { /* if it does exist, but we can't read it, don't try writing */ - if (mch_stat((char *)fname, &st_new) == 0) + if (os_file_exists(fname)) goto end; #if defined(UNIX) /* @@ -1536,16 +1532,15 @@ void write_viminfo(char_u *file, int forceit) * overwrite a user's viminfo file after a "su root", with a * viminfo file that the user can't read. */ - st_old.st_dev = (dev_t)0; - st_old.st_ino = 0; - st_old.st_mode = 0600; - if (mch_stat((char *)fname, &st_old) == 0 + + FileInfo old_info; // FileInfo of existing viminfo file + if (os_get_file_info((char *)fname, &old_info) && getuid() != ROOT_UID - && !(st_old.st_uid == getuid() - ? (st_old.st_mode & 0200) - : (st_old.st_gid == getgid() - ? (st_old.st_mode & 0020) - : (st_old.st_mode & 0002)))) { + && !(old_info.stat.st_uid == getuid() + ? (old_info.stat.st_mode & 0200) + : (old_info.stat.st_gid == getgid() + ? (old_info.stat.st_mode & 0020) + : (old_info.stat.st_mode & 0002)))) { int tt = msg_didany; /* avoid a wait_return for this message, it's annoying */ @@ -1563,7 +1558,7 @@ void write_viminfo(char_u *file, int forceit) * Check if tempfile already exists. Never overwrite an * existing file! */ - if (mch_stat((char *)tempname, &st_new) == 0) { + if (os_file_exists(tempname)) { /* * Try another name. Change one character, just before * the extension. @@ -1571,8 +1566,7 @@ void write_viminfo(char_u *file, int forceit) wp = tempname + STRLEN(tempname) - 5; if (wp < path_tail(tempname)) /* empty file name? */ wp = path_tail(tempname); - for (*wp = 'z'; mch_stat((char *)tempname, &st_new) == 0; - --*wp) { + for (*wp = 'z'; os_file_exists(tempname); --*wp) { /* * They all exist? Must be something wrong! Don't * write the viminfo file then. @@ -1598,7 +1592,7 @@ void write_viminfo(char_u *file, int forceit) umask_save = umask(0); fd = mch_open((char *)tempname, O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, - (int)((st_old.st_mode & 0777) | 0600)); + (int)((old_info.stat.st_mode & 0777) | 0600)); (void)umask(umask_save); # else fd = mch_open((char *)tempname, @@ -1624,8 +1618,9 @@ void write_viminfo(char_u *file, int forceit) * Make sure the owner can read/write it. This only works for * root. */ - if (fp_out != NULL) - ignored = fchown(fileno(fp_out), st_old.st_uid, st_old.st_gid); + if (fp_out != NULL) { + fchown(fileno(fp_out), old_info.stat.st_uid, old_info.stat.st_gid); + } #endif } } diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c index aecd9995ba..0837e5386e 100644 --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -48,15 +48,13 @@ static void cmd_source(char_u *fname, exarg_T *eap); /* Growarray to store info about already sourced scripts. - * For Unix also store the dev/ino, so that we don't have to stat() each + * Also store the dev/ino, so that we don't have to stat() each * script when going through the list. */ typedef struct scriptitem_S { char_u *sn_name; -# ifdef UNIX int sn_dev_valid; - dev_t sn_dev; - ino_t sn_ino; -# endif + uint64_t sn_dev; + uint64_t sn_ino; int sn_prof_on; /* TRUE when script is/was profiled */ int sn_pr_force; /* forceit: profile functions in this script */ proftime_T sn_pr_child; /* time set when going into first child */ @@ -2458,10 +2456,6 @@ do_source ( void *save_funccalp; int save_debug_break_level = debug_break_level; scriptitem_T *si = NULL; -# ifdef UNIX - struct stat st; - int stat_ok; -# endif #ifdef STARTUPTIME struct timeval tv_rel; struct timeval tv_start; @@ -2622,23 +2616,19 @@ do_source ( * If it's new, generate a new SID. */ save_current_SID = current_SID; -# ifdef UNIX - stat_ok = (mch_stat((char *)fname_exp, &st) >= 0); -# endif + FileInfo file_info; + bool file_info_ok = os_get_file_info((char *)fname_exp, &file_info); for (current_SID = script_items.ga_len; current_SID > 0; --current_SID) { si = &SCRIPT_ITEM(current_SID); + // Compare dev/ino when possible, it catches symbolic links. + // Also compare file names, the inode may change when the file was edited. + bool file_id_equal = file_info_ok && si->sn_dev_valid + && si->sn_dev == file_info.stat.st_dev + && si->sn_ino == file_info.stat.st_ino; if (si->sn_name != NULL - && ( -# ifdef UNIX - /* Compare dev/ino when possible, it catches symbolic - * links. Also compare file names, the inode may change - * when the file was edited. */ - ((stat_ok && si->sn_dev_valid) - && (si->sn_dev == st.st_dev - && si->sn_ino == st.st_ino)) || -# endif - fnamecmp(si->sn_name, fname_exp) == 0)) + && (file_id_equal || fnamecmp(si->sn_name, fname_exp) == 0)) { break; + } } if (current_SID == 0) { current_SID = ++last_current_SID; @@ -2651,14 +2641,13 @@ do_source ( si = &SCRIPT_ITEM(current_SID); si->sn_name = fname_exp; fname_exp = NULL; -# ifdef UNIX - if (stat_ok) { + if (file_info_ok) { si->sn_dev_valid = TRUE; - si->sn_dev = st.st_dev; - si->sn_ino = st.st_ino; - } else + si->sn_dev = file_info.stat.st_dev; + si->sn_ino = file_info.stat.st_ino; + } else { si->sn_dev_valid = FALSE; -# endif + } /* Allocate the local script variables to use for this script. */ new_script_vars(current_SID); diff --git a/src/file_search.c b/src/file_search.c index 82b9689e09..96f0b71b73 100644 --- a/src/file_search.c +++ b/src/file_search.c @@ -110,11 +110,9 @@ typedef struct ff_visited { /* for unix use inode etc for comparison (needed because of links), else * use filename. */ -#ifdef UNIX int ffv_dev_valid; /* ffv_dev and ffv_ino were set */ - dev_t ffv_dev; /* device number */ - ino_t ffv_ino; /* inode number */ -#endif + uint64_t ffv_dev; /* device number */ + uint64_t ffv_ino; /* inode number */ /* The memory for this struct is allocated according to the length of * ffv_fname. */ @@ -1126,38 +1124,27 @@ static int ff_wc_equal(char_u *s1, char_u *s2) static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *wc_path) { ff_visited_T *vp; -#ifdef UNIX - struct stat st; - int url = FALSE; -#endif + bool url = false; - /* For an URL we only compare the name, otherwise we compare the - * device/inode (unix) or the full path name (not Unix). */ + FileInfo file_info; + // For an URL we only compare the name, otherwise we compare the + // device/inode. if (path_with_url(fname)) { vim_strncpy(ff_expand_buffer, fname, MAXPATHL - 1); -#ifdef UNIX - url = TRUE; -#endif + url = true; } else { ff_expand_buffer[0] = NUL; -#ifdef UNIX - if (mch_stat((char *)fname, &st) < 0) -#else - if (vim_FullName(fname, ff_expand_buffer, MAXPATHL, TRUE) == FAIL) -#endif + if (!os_get_file_info((char *)fname, &file_info)) { return FAIL; + } } /* check against list of already visited files */ for (vp = *visited_list; vp != NULL; vp = vp->ffv_next) { - if ( -#ifdef UNIX - !url ? (vp->ffv_dev_valid && vp->ffv_dev == st.st_dev - && vp->ffv_ino == st.st_ino) - : -#endif - fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0 - ) { + if ((url && fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0) + || (!url && vp->ffv_dev_valid + && vp->ffv_dev == file_info.stat.st_dev + && vp->ffv_ino == file_info.stat.st_ino)) { /* are the wildcard parts equal */ if (ff_wc_equal(vp->ffv_wc_path, wc_path) == TRUE) /* already visited */ @@ -1171,19 +1158,15 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u * vp = (ff_visited_T *)alloc((unsigned)(sizeof(ff_visited_T) + STRLEN(ff_expand_buffer))); -#ifdef UNIX if (!url) { vp->ffv_dev_valid = TRUE; - vp->ffv_ino = st.st_ino; - vp->ffv_dev = st.st_dev; + vp->ffv_ino = file_info.stat.st_ino; + vp->ffv_dev = file_info.stat.st_dev; vp->ffv_fname[0] = NUL; } else { vp->ffv_dev_valid = FALSE; STRCPY(vp->ffv_fname, ff_expand_buffer); } -#else - STRCPY(vp->ffv_fname, ff_expand_buffer); -#endif if (wc_path != NULL) vp->ffv_wc_path = vim_strsave(wc_path); diff --git a/src/fileio.c b/src/fileio.c index 8f2639064a..fc23d3765c 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -86,7 +86,7 @@ static void set_file_time(char_u *fname, time_t atime, time_t mtime); static int set_rw_fname(char_u *fname, char_u *sfname); static int msg_add_fileformat(int eol_type); static void msg_add_eol(void); -static int check_mtime(buf_T *buf, struct stat *s); +static int check_mtime(buf_T *buf, FileInfo *file_info); static int time_differs(long t1, long t2); static int apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, @@ -262,7 +262,6 @@ readfile ( #endif int fileformat = 0; /* end-of-line format */ int keep_fileformat = FALSE; - struct stat st; int file_readonly; linenr_T skip_count = 0; linenr_T read_count = 0; @@ -441,8 +440,9 @@ readfile ( if (newfile && !read_stdin && !read_buffer) { /* Remember time of file. */ - if (mch_stat((char *)fname, &st) >= 0) { - buf_store_time(curbuf, &st, fname); + FileInfo file_info; + if (os_get_file_info((char *)fname, &file_info)) { + buf_store_file_info(curbuf, &file_info, fname); curbuf->b_mtime_read = curbuf->b_mtime; #ifdef UNIX /* @@ -456,7 +456,7 @@ readfile ( * not be able to write to the file ourselves. * Setting the bits is done below, after creating the swap file. */ - swap_mode = (st.st_mode & 0644) | 0600; + swap_mode = (file_info.stat.st_mode & 0644) | 0600; #endif } else { curbuf->b_mtime = 0; @@ -2473,7 +2473,6 @@ buf_write ( int overwriting; /* TRUE if writing over original */ int no_eol = FALSE; /* no end-of-line written */ int device = FALSE; /* writing to a device */ - struct stat st_old; int prev_got_int = got_int; bool file_readonly = false; /* overwritten file is read-only */ static char *err_readonly = @@ -2774,15 +2773,14 @@ buf_write ( * Get information about original file (if there is one). */ #if defined(UNIX) - st_old.st_dev = 0; - st_old.st_ino = 0; perm = -1; - if (mch_stat((char *)fname, &st_old) < 0) + FileInfo file_info_old; + if (!os_get_file_info((char *)fname, &file_info_old)) { newfile = TRUE; - else { - perm = st_old.st_mode; - if (!S_ISREG(st_old.st_mode)) { /* not a file */ - if (S_ISDIR(st_old.st_mode)) { + } else { + perm = file_info_old.stat.st_mode; + if (!S_ISREG(file_info_old.stat.st_mode)) { /* not a file */ + if (S_ISDIR(file_info_old.stat.st_mode)) { errnum = (char_u *)"E502: "; errmsg = (char_u *)_("is a directory"); goto fail; @@ -2822,8 +2820,10 @@ buf_write ( errmsg = (char_u *)_("is a directory"); goto fail; } - if (overwriting) - (void)mch_stat((char *)fname, &st_old); + if (overwriting) { + os_get_file_info((char *)fname, &file_info_old); + } + } #endif /* !UNIX */ @@ -2849,7 +2849,7 @@ buf_write ( * Check if the timestamp hasn't changed since reading the file. */ if (overwriting) { - retval = check_mtime(buf, &st_old); + retval = check_mtime(buf, &file_info_old); if (retval == FAIL) goto fail; } @@ -2890,14 +2890,11 @@ buf_write ( * off. This helps when editing large files on almost-full disks. */ if (!(append && *p_pm == NUL) && !filtering && perm >= 0 && dobackup) { -#if defined(UNIX) || defined(WIN32) - struct stat st; -#endif + FileInfo file_info; - if ((bkc_flags & BKC_YES) || append) /* "yes" */ + if ((bkc_flags & BKC_YES) || append) { /* "yes" */ backup_copy = TRUE; -#if defined(UNIX) || defined(WIN32) - else if ((bkc_flags & BKC_AUTO)) { /* "auto" */ + } else if ((bkc_flags & BKC_AUTO)) { /* "auto" */ int i; # ifdef UNIX @@ -2908,18 +2905,16 @@ buf_write ( * - we don't have write permission in the directory * - we can't set the owner/group of the new file */ - if (st_old.st_nlink > 1 - || mch_lstat((char *)fname, &st) < 0 - || st.st_dev != st_old.st_dev - || st.st_ino != st_old.st_ino + if (file_info_old.stat.st_nlink > 1 + || !os_get_file_info_link((char *)fname, &file_info) + || !os_file_info_id_equal(&file_info, &file_info_old) # ifndef HAVE_FCHOWN - || st.st_uid != st_old.st_uid - || st.st_gid != st_old.st_gid + || file_info.stat.st_uid != file_info_old.stat.st_uid + || file_info.stat.st_gid != file_info_old.stat.st_gid # endif - ) + ) { backup_copy = TRUE; - else -# else + } else # endif { /* @@ -2931,8 +2926,9 @@ buf_write ( STRCPY(IObuff, fname); for (i = 4913;; i += 123) { sprintf((char *)path_tail(IObuff), "%d", i); - if (mch_lstat((char *)IObuff, &st) < 0) + if (!os_get_file_info_link((char *)IObuff, &file_info)) { break; + } } fd = mch_open((char *)IObuff, O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, perm); @@ -2941,13 +2937,14 @@ buf_write ( else { # ifdef UNIX # ifdef HAVE_FCHOWN - ignored = fchown(fd, st_old.st_uid, st_old.st_gid); + fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid); # endif - if (mch_stat((char *)IObuff, &st) < 0 - || st.st_uid != st_old.st_uid - || st.st_gid != st_old.st_gid - || (long)st.st_mode != perm) + if (!os_get_file_info((char *)IObuff, &file_info) + || file_info.stat.st_uid != file_info_old.stat.st_uid + || file_info.stat.st_gid != file_info_old.stat.st_gid + || (long)file_info.stat.st_mode != perm) { backup_copy = TRUE; + } # endif /* Close the file before removing it, on MS-Windows we * can't delete an open file. */ @@ -2962,27 +2959,25 @@ buf_write ( */ if ((bkc_flags & BKC_BREAKSYMLINK) || (bkc_flags & BKC_BREAKHARDLINK)) { # ifdef UNIX - int lstat_res; - - lstat_res = mch_lstat((char *)fname, &st); + bool file_info_link_ok = os_get_file_info_link((char *)fname, &file_info); /* Symlinks. */ if ((bkc_flags & BKC_BREAKSYMLINK) - && lstat_res == 0 - && st.st_ino != st_old.st_ino) + && file_info_link_ok + && !os_file_info_id_equal(&file_info, &file_info_old)) { backup_copy = FALSE; + } /* Hardlinks. */ if ((bkc_flags & BKC_BREAKHARDLINK) - && st_old.st_nlink > 1 - && (lstat_res != 0 || st.st_ino == st_old.st_ino)) + && file_info_old.stat.st_nlink > 1 + && (!file_info_link_ok + || os_file_info_id_equal(&file_info, &file_info_old))) { backup_copy = FALSE; -# else + } # endif } -#endif - /* make sure we have a valid backup extension to use */ if (*p_bex == NUL) backup_ext = (char_u *)".bak"; @@ -2994,7 +2989,6 @@ buf_write ( int bfd; char_u *copybuf, *wp; int some_error = FALSE; - struct stat st_new; char_u *dirp; char_u *rootname; @@ -3019,12 +3013,6 @@ buf_write ( */ dirp = p_bdir; while (*dirp) { -#ifdef UNIX - st_new.st_ino = 0; - st_new.st_dev = 0; - st_new.st_gid = 0; -#endif - /* * Isolate one directory name, using an entry in 'bdir'. */ @@ -3035,6 +3023,7 @@ buf_write ( goto nobackup; } + FileInfo file_info_new; { /* * Make backup file name. @@ -3049,20 +3038,17 @@ buf_write ( /* * Check if backup file already exists. */ - if (mch_stat((char *)backup, &st_new) >= 0) { -#ifdef UNIX + if (os_get_file_info((char *)backup, &file_info_new)) { /* * Check if backup file is same as original file. * May happen when modname() gave the same file back (e.g. silly * link). If we don't check here, we either ruin the file when * copying or erase it after writing. */ - if (st_new.st_dev == st_old.st_dev - && st_new.st_ino == st_old.st_ino) { + if (os_file_info_id_equal(&file_info_new, &file_info_old)) { free(backup); backup = NULL; /* no backup file to delete */ } -#endif /* * If we are not going to keep the backup file, don't @@ -3076,8 +3062,9 @@ buf_write ( wp = backup; *wp = 'z'; while (*wp > 'a' - && mch_stat((char *)backup, &st_new) >= 0) + && os_get_file_info((char *)backup, &file_info_new)) { --*wp; + } /* They all exist??? Must be something wrong. */ if (*wp == 'a') { free(backup); @@ -3114,13 +3101,13 @@ buf_write ( * bits for the group same as the protection bits for * others. */ - if (st_new.st_gid != st_old.st_gid + if (file_info_new.stat.st_gid != file_info_old.stat.st_gid # ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */ - && fchown(bfd, (uid_t)-1, st_old.st_gid) != 0 + && fchown(bfd, (uid_t)-1, file_info_old.stat.st_gid) != 0 # endif - ) - os_setperm(backup, - (perm & 0707) | ((perm & 07) << 3)); + ) { + os_setperm(backup, (perm & 0707) | ((perm & 07) << 3)); + } # ifdef HAVE_SELINUX mch_copy_sec(fname, backup); # endif @@ -3155,7 +3142,9 @@ buf_write ( errmsg = (char_u *)_( "E508: Can't read file for backup (add ! to override)"); #ifdef UNIX - set_file_time(backup, st_old.st_atime, st_old.st_mtime); + set_file_time(backup, + file_info_old.stat.st_atim.tv_sec, + file_info_old.stat.st_mtim.tv_sec); #endif #ifdef HAVE_ACL mch_set_acl(backup, acl); @@ -3266,7 +3255,8 @@ nobackup: #if defined(UNIX) /* When using ":w!" and the file was read-only: make it writable */ - if (forceit && perm >= 0 && !(perm & 0200) && st_old.st_uid == getuid() + if (forceit && perm >= 0 && !(perm & 0200) + && file_info_old.stat.st_uid == getuid() && vim_strchr(p_cpo, CPO_FWRITE) == NULL) { perm |= 0200; (void)os_setperm(fname, perm); @@ -3412,15 +3402,14 @@ nobackup: */ if (errmsg == NULL) { #ifdef UNIX - struct stat st; + FileInfo file_info; /* Don't delete the file when it's a hard or symbolic link. */ - if ((!newfile && st_old.st_nlink > 1) - || (mch_lstat((char *)fname, &st) == 0 - && (st.st_dev != st_old.st_dev - || st.st_ino != st_old.st_ino))) + if ((!newfile && file_info_old.stat.st_nlink > 1) + || (os_get_file_info_link((char *)fname, &file_info) + && !os_file_info_id_equal(&file_info, &file_info_old))) { errmsg = (char_u *)_("E166: Can't open linked file for writing"); - else + } else #endif { errmsg = (char_u *)_("E212: Can't open file for writing"); @@ -3432,8 +3421,10 @@ nobackup: if (!(perm & 0200)) made_writable = TRUE; perm |= 0200; - if (st_old.st_uid != getuid() || st_old.st_gid != getgid()) + if (file_info_old.stat.st_uid != getuid() + || file_info_old.stat.st_gid != getgid()) { perm &= 0777; + } #endif if (!append) /* don't remove when appending */ os_remove((char *)wfname); @@ -3444,8 +3435,6 @@ nobackup: restore_backup: { - struct stat st; - /* * If we failed to open the file, we don't need a backup. Throw it * away. If we moved or removed the original file try to put the @@ -3460,11 +3449,13 @@ restore_backup: * In that case we leave the copy around. */ /* If file does not exist, put the copy in its place */ - if (mch_stat((char *)fname, &st) < 0) + if (!os_file_exists(fname)) { vim_rename(backup, fname); + } /* if original file does exist throw away the copy */ - if (mch_stat((char *)fname, &st) >= 0) + if (os_file_exists(fname)) { os_remove((char *)backup); + } } else { /* try to put the original file back */ vim_rename(backup, fname); @@ -3472,8 +3463,9 @@ restore_backup: } /* if original file no longer exists give an extra warning */ - if (!newfile && mch_stat((char *)fname, &st) < 0) + if (!newfile && !os_file_exists(fname)) { end = 0; + } } if (wfname != fname) @@ -3649,14 +3641,14 @@ restore_backup: * file. Get the new device and inode number. */ if (backup != NULL && !backup_copy) { # ifdef HAVE_FCHOWN - struct stat st; /* don't change the owner when it's already OK, some systems remove * permission or ACL stuff */ - if (mch_stat((char *)wfname, &st) < 0 - || st.st_uid != st_old.st_uid - || st.st_gid != st_old.st_gid) { - ignored = fchown(fd, st_old.st_uid, st_old.st_gid); + FileInfo file_info; + if (!os_get_file_info((char *)wfname, &file_info) + || file_info.stat.st_uid != file_info_old.stat.st_uid + || file_info.stat.st_gid != file_info_old.stat.st_gid) { + fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid); if (perm >= 0) /* set permission again, may have changed */ (void)os_setperm(wfname, perm); } @@ -3855,20 +3847,20 @@ restore_backup: char *org = (char *)modname(fname, p_pm, FALSE); if (backup != NULL) { - struct stat st; - /* * If the original file does not exist yet * the current backup file becomes the original file */ if (org == NULL) EMSG(_("E205: Patchmode: can't save original file")); - else if (mch_stat(org, &st) < 0) { + else if (!os_file_exists((char_u *)org)) { vim_rename(backup, (char_u *)org); free(backup); /* don't delete the file */ backup = NULL; #ifdef UNIX - set_file_time((char_u *)org, st_old.st_atime, st_old.st_mtime); + set_file_time((char_u *)org, + file_info_old.stat.st_atim.tv_sec, + file_info_old.stat.st_mtim.tv_sec); #endif } } @@ -3961,8 +3953,8 @@ nofail: /* Update the timestamp to avoid an "overwrite changed file" * prompt when writing again. */ - if (mch_stat((char *)fname, &st_old) >= 0) { - buf_store_time(buf, &st_old, fname); + if (os_get_file_info((char *)fname, &file_info_old)) { + buf_store_file_info(buf, &file_info_old, fname); buf->b_mtime_read = buf->b_mtime; } } @@ -4138,10 +4130,11 @@ static void msg_add_eol(void) * The size isn't checked, because using a tool like "gzip" takes care of * using the same timestamp but can't set the size. */ -static int check_mtime(buf_T *buf, struct stat *st) +static int check_mtime(buf_T *buf, FileInfo *file_info) { if (buf->b_mtime_read != 0 - && time_differs((long)st->st_mtime, buf->b_mtime_read)) { + && time_differs((long)file_info->stat.st_mtim.tv_sec, + buf->b_mtime_read)) { msg_scroll = TRUE; /* don't overwrite messages here */ msg_silent = 0; /* must give this prompt */ /* don't use emsg() here, don't want to flush the buffers */ @@ -4814,12 +4807,11 @@ int vim_rename(char_u *from, char_u *to) int n; char *errmsg = NULL; char *buffer; - struct stat st; long perm; #ifdef HAVE_ACL vim_acl_T acl; /* ACL from original file */ #endif - int use_tmp_file = FALSE; + bool use_tmp_file = false; /* * When the names are identical, there is nothing to do. When they refer @@ -4828,30 +4820,25 @@ int vim_rename(char_u *from, char_u *to) */ if (fnamecmp(from, to) == 0) { if (p_fic && STRCMP(path_tail(from), path_tail(to)) != 0) - use_tmp_file = TRUE; + use_tmp_file = true; else return 0; } - /* - * Fail if the "from" file doesn't exist. Avoids that "to" is deleted. - */ - if (mch_stat((char *)from, &st) < 0) + // Fail if the "from" file doesn't exist. Avoids that "to" is deleted. + FileInfo from_info; + if (!os_get_file_info((char *)from, &from_info)) { return -1; + } -#ifdef UNIX - { - struct stat st_to; - - /* It's possible for the source and destination to be the same file. - * This happens when "from" and "to" differ in case and are on a FAT32 - * filesystem. In that case go through a temp file name. */ - if (mch_stat((char *)to, &st_to) >= 0 - && st.st_dev == st_to.st_dev - && st.st_ino == st_to.st_ino) - use_tmp_file = TRUE; + // It's possible for the source and destination to be the same file. + // This happens when "from" and "to" differ in case and are on a FAT32 + // filesystem. In that case go through a temp file name. + FileInfo to_info; + if (os_get_file_info((char *)to, &to_info) + && os_file_info_id_equal(&from_info, &to_info)) { + use_tmp_file = true; } -#endif if (use_tmp_file) { char_u tempname[MAXPATHL + 1]; @@ -5086,8 +5073,6 @@ buf_check_timestamp ( int focus /* called for GUI focus event */ ) { - struct stat st; - int stat_res; int retval = 0; char_u *path; char_u *tbuf; @@ -5114,27 +5099,25 @@ buf_check_timestamp ( ) return 0; - if ( !(buf->b_flags & BF_NOTEDITED) - && buf->b_mtime != 0 - && ((stat_res = mch_stat((char *)buf->b_ffname, &st)) < 0 - || time_differs((long)st.st_mtime, buf->b_mtime) - || st.st_size != buf->b_orig_size -#ifdef HAVE_ST_MODE - || (int)st.st_mode != buf->b_orig_mode -#else - || os_getperm(buf->b_ffname) != buf->b_orig_mode -#endif - )) { + FileInfo file_info; + bool file_info_ok; + if (!(buf->b_flags & BF_NOTEDITED) + && buf->b_mtime != 0 + && (!(file_info_ok = os_get_file_info((char *)buf->b_ffname, &file_info)) + || time_differs((long)file_info.stat.st_mtim.tv_sec, buf->b_mtime) + || (int)file_info.stat.st_mode != buf->b_orig_mode + )) { retval = 1; /* set b_mtime to stop further warnings (e.g., when executing * FileChangedShell autocmd) */ - if (stat_res < 0) { + if (!file_info_ok) { buf->b_mtime = 0; buf->b_orig_size = 0; buf->b_orig_mode = 0; - } else - buf_store_time(buf, &st, buf->b_ffname); + } else { + buf_store_file_info(buf, &file_info, buf->b_ffname); + } /* Don't do anything for a directory. Might contain the file * explorer. */ @@ -5147,10 +5130,10 @@ buf_check_timestamp ( * was set, the global option value otherwise. */ else if ((buf->b_p_ar >= 0 ? buf->b_p_ar : p_ar) - && !bufIsChanged(buf) && stat_res >= 0) + && !bufIsChanged(buf) && file_info_ok) reload = TRUE; else { - if (stat_res < 0) + if (!file_info_ok) reason = "deleted"; else if (bufIsChanged(buf)) reason = "conflict"; @@ -5435,15 +5418,12 @@ void buf_reload(buf_T *buf, int orig_mode) /* Careful: autocommands may have made "buf" invalid! */ } -void buf_store_time(buf_T *buf, struct stat *st, char_u *fname) +// TODO(stefan991): remove unused parameter fname +void buf_store_file_info(buf_T *buf, FileInfo *file_info, char_u *fname) { - buf->b_mtime = (long)st->st_mtime; - buf->b_orig_size = st->st_size; -#ifdef HAVE_ST_MODE - buf->b_orig_mode = (int)st->st_mode; -#else - buf->b_orig_mode = os_getperm(fname); -#endif + buf->b_mtime = (long)file_info->stat.st_mtim.tv_sec; + buf->b_orig_size = file_info->stat.st_size; + buf->b_orig_mode = (int)file_info->stat.st_mode; } /* @@ -5527,9 +5507,6 @@ vim_tempname ( #ifdef TEMPDIRNAMES static char *(tempdirs[]) = {TEMPDIRNAMES}; int i; -# ifndef EEXIST - struct stat st; -# endif /* * This will create a directory for private use by this instance of Vim. @@ -5580,7 +5557,7 @@ vim_tempname ( /* If mkdir() does not set errno to EEXIST, check for * existing file here. There is a race condition then, * although it's fail-safe. */ - if (mch_stat((char *)itmp, &st) >= 0) + if (os_file_exists(itmp)) continue; # endif # if defined(UNIX) diff --git a/src/fileio.h b/src/fileio.h index 8b743aab0d..5f67449382 100644 --- a/src/fileio.h +++ b/src/fileio.h @@ -2,6 +2,7 @@ #define NEOVIM_FILEIO_H #include "buffer_defs.h" +#include "os/os.h" /* * Struct to save values in before executing autocommands for a buffer that is @@ -40,7 +41,7 @@ int vim_rename(char_u *from, char_u *to); int check_timestamps(int focus); int buf_check_timestamp(buf_T *buf, int focus); void buf_reload(buf_T *buf, int orig_mode); -void buf_store_time(buf_T *buf, struct stat *st, char_u *fname); +void buf_store_file_info(buf_T *buf, FileInfo *file_info, char_u *fname); void write_lnum_adjust(linenr_T offset); void vim_deltempdir(void); char_u *vim_tempname(int extra_char); diff --git a/src/if_cscope.c b/src/if_cscope.c index eadd1e5b1a..b000b03444 100644 --- a/src/if_cscope.c +++ b/src/if_cscope.c @@ -52,8 +52,7 @@ static int cs_find_common(char *opt, char *pat, int, int, int, char_u *cmdline); static int cs_help(exarg_T *eap); static void clear_csinfo(int i); -static int cs_insert_filelist(char *, char *, char *, - struct stat *); +static int cs_insert_filelist(char *, char *, char *, FileInfo *file_info); static int cs_kill(exarg_T *eap); static void cs_kill_execute(int, char *); static cscmd_T * cs_lookup_cmd(exarg_T *eap); @@ -481,8 +480,6 @@ cs_add_common ( char *flags ) { - struct stat statbuf; - int ret; char *fname = NULL; char *fname2 = NULL; char *ppath = NULL; @@ -503,28 +500,25 @@ cs_add_common ( goto add_err; fname = (char *)vim_strnsave((char_u *)fname, len); free(fbuf); - ret = stat(fname, &statbuf); - if (ret < 0) { + FileInfo file_info; + bool file_info_ok = os_get_file_info(fname, &file_info); + if (!file_info_ok) { staterr: if (p_csverbose) cs_stat_emsg(fname); goto add_err; } - /* get the prepend path (arg2), expand it, and try to stat it */ + // get the prepend path (arg2), expand it, and see if it exists if (arg2 != NULL) { - struct stat statbuf2; - ppath = (char *)alloc(MAXPATHL + 1); - expand_env((char_u *)arg2, (char_u *)ppath, MAXPATHL); - ret = stat(ppath, &statbuf2); - if (ret < 0) + if (!os_file_exists((char_u *)ppath)) goto staterr; } /* if filename is a directory, append the cscope database name to it */ - if ((statbuf.st_mode & S_IFMT) == S_IFDIR) { + if ((file_info.stat.st_mode & S_IFMT) == S_IFDIR) { fname2 = (char *)alloc((unsigned)(strlen(CSCOPE_DBFILE) + strlen(fname) + 2)); while (fname[strlen(fname)-1] == '/' @@ -538,23 +532,18 @@ staterr: else (void)sprintf(fname2, "%s/%s", fname, CSCOPE_DBFILE); - ret = stat(fname2, &statbuf); - if (ret < 0) { + file_info_ok = os_get_file_info(fname2, &file_info); + if (!file_info_ok) { if (p_csverbose) cs_stat_emsg(fname2); goto add_err; } - i = cs_insert_filelist(fname2, ppath, flags, &statbuf); + i = cs_insert_filelist(fname2, ppath, flags, &file_info); } -#if defined(UNIX) - else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) -#else - /* WIN32 - substitute define S_ISREG from os_unix_defs.h */ - else if (((statbuf.st_mode) & S_IFMT) == S_IFREG) -#endif + else if (S_ISREG(file_info.stat.st_mode) || S_ISLNK(file_info.stat.st_mode)) { - i = cs_insert_filelist(fname, ppath, flags, &statbuf); + i = cs_insert_filelist(fname, ppath, flags, &file_info); } else { if (p_csverbose) (void)EMSG2( @@ -1223,53 +1212,16 @@ static char *GetWin32Error(void) * * insert a new cscope database filename into the filelist */ -static int cs_insert_filelist(char *fname, char *ppath, char *flags, struct stat *sb) +static int cs_insert_filelist(char *fname, char *ppath, char *flags, + FileInfo *file_info) { short i, j; -#ifndef UNIX - BY_HANDLE_FILE_INFORMATION bhfi; - - /* On windows 9x GetFileInformationByHandle doesn't work, so skip it */ - if (!mch_windows95()) { - switch (win32_fileinfo(fname, &bhfi)) { - case FILEINFO_ENC_FAIL: /* enc_to_utf16() failed */ - case FILEINFO_READ_FAIL: /* CreateFile() failed */ - if (p_csverbose) { - char *cant_msg = _("E625: cannot open cscope database: %s"); - char *winmsg = GetWin32Error(); - - if (winmsg != NULL) { - (void)EMSG2(cant_msg, winmsg); - LocalFree(winmsg); - } else - /* subst filename if can't get error text */ - (void)EMSG2(cant_msg, fname); - } - return -1; - - case FILEINFO_INFO_FAIL: /* GetFileInformationByHandle() failed */ - if (p_csverbose) - (void)EMSG(_("E626: cannot get cscope database information")); - return -1; - } - } -#endif i = -1; /* can be set to the index of an empty item in csinfo */ for (j = 0; j < csinfo_size; j++) { if (csinfo[j].fname != NULL -#if defined(UNIX) - && csinfo[j].st_dev == sb->st_dev && csinfo[j].st_ino == sb->st_ino -#else - /* compare pathnames first */ - && ((path_full_compare(csinfo[j].fname, fname, FALSE) & kEqualFiles) - /* if not Windows 9x, test index file attributes too */ - || (!mch_windows95() - && csinfo[j].nVolume == bhfi.dwVolumeSerialNumber - && csinfo[j].nIndexHigh == bhfi.nFileIndexHigh - && csinfo[j].nIndexLow == bhfi.nFileIndexLow)) -#endif - ) { + && csinfo[j].st_dev == file_info->stat.st_dev + && csinfo[j].st_ino == file_info->stat.st_ino) { if (p_csverbose) (void)EMSG(_("E568: duplicate cscope database not added")); return -1; @@ -1312,15 +1264,8 @@ static int cs_insert_filelist(char *fname, char *ppath, char *flags, struct stat } else csinfo[i].flags = NULL; -#if defined(UNIX) - csinfo[i].st_dev = sb->st_dev; - csinfo[i].st_ino = sb->st_ino; - -#else - csinfo[i].nVolume = bhfi.dwVolumeSerialNumber; - csinfo[i].nIndexLow = bhfi.nFileIndexLow; - csinfo[i].nIndexHigh = bhfi.nFileIndexHigh; -#endif + csinfo[i].st_dev = file_info->stat.st_dev; + csinfo[i].st_ino = file_info->stat.st_ino; return i; } /* cs_insert_filelist */ diff --git a/src/if_cscope_defs.h b/src/if_cscope_defs.h index 7a60da20dd..3c64067795 100644 --- a/src/if_cscope_defs.h +++ b/src/if_cscope_defs.h @@ -51,10 +51,9 @@ typedef struct csi { char * flags; /* additional cscope flags/options (e.g, -p2) */ #if defined(UNIX) pid_t pid; /* PID of the connected cscope process. */ - dev_t st_dev; /* ID of dev containing cscope db */ - ino_t st_ino; /* inode number of cscope db */ -#else #endif + uint64_t st_dev; /* ID of dev containing cscope db */ + uint64_t st_ino; /* inode number of cscope db */ FILE * fr_fp; /* from cscope: FILE. */ FILE * to_fp; /* to cscope: FILE. */ diff --git a/src/macros.h b/src/macros.h index 5f7147be23..9bfe3d054b 100644 --- a/src/macros.h +++ b/src/macros.h @@ -98,21 +98,6 @@ #define vim_isbreak(c) (breakat_flags[(char_u)(c)]) # define mch_fopen(n, p) fopen((n), (p)) -# define mch_fstat(n, p) fstat((n), (p)) -# ifdef STAT_IGNORES_SLASH -/* On Solaris stat() accepts "file/" as if it was "file". Return -1 if - * the name ends in "/" and it's not a directory. */ -# define mch_stat(n, p) (illegal_slash(n) ? -1 : stat((n), (p))) -# else -# define mch_stat(n, p) stat((n), (p)) -# endif - -#ifdef HAVE_LSTAT -# define mch_lstat(n, p) lstat((n), (p)) -#else -# define mch_lstat(n, p) mch_stat((n), (p)) -#endif - # define mch_open(n, m, p) open((n), (m), (p)) /* mch_open_rw(): invoke mch_open() with third argument for user R/W. */ diff --git a/src/main.c b/src/main.c index 9fd97b23a5..828a6d5200 100644 --- a/src/main.c +++ b/src/main.c @@ -2130,14 +2130,13 @@ process_env ( */ static int file_owned(char *fname) { - struct stat s; uid_t uid = getuid(); - - return !(mch_stat(fname, &s) != 0 || s.st_uid != uid -# ifdef HAVE_LSTAT - || mch_lstat(fname, &s) != 0 || s.st_uid != uid -# endif - ); + FileInfo file_info; + bool file_owned = os_get_file_info(fname, &file_info) + && file_info.stat.st_uid == uid; + bool link_owned = os_get_file_info_link(fname, &file_info) + && file_info.stat.st_uid == uid; + return file_owned && link_owned; } #endif diff --git a/src/memfile.c b/src/memfile.c index 5330fb7fc5..9431101d8d 100644 --- a/src/memfile.c +++ b/src/memfile.c @@ -46,25 +46,6 @@ #include "ui.h" #include "os/os.h" -/* - * Some systems have the page size in statfs.f_bsize, some in stat.st_blksize - */ -#ifdef HAVE_ST_BLKSIZE -# define STATFS stat -# define F_BSIZE st_blksize -# define fstatfs(fd, buf, len, nul) mch_fstat((fd), (buf)) -#else -# ifdef HAVE_SYS_STATFS_H -# include <sys/statfs.h> -# define STATFS statfs -# define F_BSIZE f_bsize -# endif -#endif - -/* - * for Amiga Dos 2.0x we use Flush - */ - #define MEMFILE_PAGE_SIZE 4096 /* default page size */ static long_u total_mem_used = 0; /* total memory used for memfiles */ @@ -125,10 +106,6 @@ memfile_T *mf_open(char_u *fname, int flags) { memfile_T *mfp; off_t size; -#if defined(STATFS) && defined(UNIX) && !defined(__QNX__) && !defined(__minix) -# define USE_FSTATFS - struct STATFS stf; -#endif if ((mfp = (memfile_T *)alloc((unsigned)sizeof(memfile_T))) == NULL) return NULL; @@ -157,7 +134,6 @@ memfile_T *mf_open(char_u *fname, int flags) mfp->mf_page_size = MEMFILE_PAGE_SIZE; mfp->mf_old_key = NULL; -#ifdef USE_FSTATFS /* * Try to set the page size equal to the block size of the device. * Speeds up I/O a lot. @@ -165,12 +141,13 @@ memfile_T *mf_open(char_u *fname, int flags) * in ml_recover(). The size used here may be wrong, therefore * mf_blocknr_max must be rounded up. */ + FileInfo file_info; if (mfp->mf_fd >= 0 - && fstatfs(mfp->mf_fd, &stf, sizeof(struct statfs), 0) == 0 - && stf.F_BSIZE >= MIN_SWAP_PAGE_SIZE - && stf.F_BSIZE <= MAX_SWAP_PAGE_SIZE) - mfp->mf_page_size = stf.F_BSIZE; -#endif + && os_get_file_info_fd(mfp->mf_fd, &file_info) + && file_info.stat.st_blksize >= MIN_SWAP_PAGE_SIZE + && file_info.stat.st_blksize <= MAX_SWAP_PAGE_SIZE) { + mfp->mf_page_size = file_info.stat.st_blksize; + } if (mfp->mf_fd < 0 || (flags & (O_TRUNC|O_EXCL)) || (size = lseek(mfp->mf_fd, (off_t)0L, SEEK_END)) <= 0) @@ -1064,10 +1041,6 @@ mf_do_open ( int flags /* flags for open() */ ) { -#ifdef HAVE_LSTAT - struct stat sb; -#endif - mfp->mf_fname = fname; /* @@ -1077,17 +1050,16 @@ mf_do_open ( */ mf_set_ffname(mfp); -#ifdef HAVE_LSTAT /* * Extra security check: When creating a swap file it really shouldn't * exist yet. If there is a symbolic link, this is most likely an attack. */ - if ((flags & O_CREAT) && mch_lstat((char *)mfp->mf_fname, &sb) >= 0) { + FileInfo file_info; + if ((flags & O_CREAT) + && os_get_file_info_link((char *)mfp->mf_fname, &file_info)) { mfp->mf_fd = -1; EMSG(_("E300: Swap file already exists (symlink attack?)")); - } else -#endif - { + } else { /* * try to open the file */ diff --git a/src/memline.c b/src/memline.c index 53864e6b5d..bf2743fe1d 100644 --- a/src/memline.c +++ b/src/memline.c @@ -832,8 +832,6 @@ static void ml_upd_block0(buf_T *buf, upd_block0_T what) */ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf) { - struct stat st; - if (buf->b_ffname == NULL) b0p->b0_fname[0] = NUL; else { @@ -861,12 +859,13 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf) memmove(b0p->b0_fname + 1, uname, ulen); } } - if (mch_stat((char *)buf->b_ffname, &st) >= 0) { - long_to_char((long)st.st_mtime, b0p->b0_mtime); + FileInfo file_info; + if (os_get_file_info((char *)buf->b_ffname, &file_info)) { + long_to_char((long)file_info.stat.st_mtim.tv_sec, b0p->b0_mtime); #ifdef CHECK_INODE - long_to_char((long)st.st_ino, b0p->b0_ino); + long_to_char((long)file_info.stat.st_ino, b0p->b0_ino); #endif - buf_store_time(buf, &st, buf->b_ffname); + buf_store_file_info(buf, &file_info, buf->b_ffname); buf->b_mtime_read = buf->b_mtime; } else { long_to_char(0L, b0p->b0_mtime); @@ -943,7 +942,6 @@ void ml_recover(void) infoptr_T *ip; blocknr_T bnum; int page_count; - struct stat org_stat, swp_stat; int len; int directly; linenr_T lnum; @@ -1155,12 +1153,15 @@ void ml_recover(void) /* * check date of swap file and original file */ + FileInfo org_file_info; + FileInfo swp_file_info; mtime = char_to_long(b0p->b0_mtime); if (curbuf->b_ffname != NULL - && mch_stat((char *)curbuf->b_ffname, &org_stat) != -1 - && ((mch_stat((char *)mfp->mf_fname, &swp_stat) != -1 - && org_stat.st_mtime > swp_stat.st_mtime) - || org_stat.st_mtime != mtime)) { + && os_get_file_info((char *)curbuf->b_ffname, &org_file_info) + && ((os_get_file_info((char *)mfp->mf_fname, &swp_file_info) + && org_file_info.stat.st_mtim.tv_sec + > swp_file_info.stat.st_mtim.tv_sec) + || org_file_info.stat.st_mtim.tv_sec != mtime)) { EMSG(_("E308: Warning: Original file may have been changed")); } out_flush(); @@ -1607,12 +1608,9 @@ recover_names ( * Try finding a swap file by simply adding ".swp" to the file name. */ if (*dirp == NUL && file_count + num_files == 0 && fname != NULL) { - struct stat st; - char_u *swapname; - - swapname = modname(fname_res, (char_u *)".swp", TRUE); + char_u *swapname = modname(fname_res, (char_u *)".swp", TRUE); if (swapname != NULL) { - if (mch_stat((char *)swapname, &st) != -1) { /* It exists! */ + if (os_file_exists(swapname)) { files = (char_u **)alloc((unsigned)sizeof(char_u *)); files[0] = swapname; swapname = NULL; @@ -1717,7 +1715,6 @@ static int process_still_running; */ static time_t swapfile_info(char_u *fname) { - struct stat st; int fd; struct block0 b0; time_t x = (time_t)0; @@ -1727,18 +1724,19 @@ static time_t swapfile_info(char_u *fname) #endif /* print the swap file date */ - if (mch_stat((char *)fname, &st) != -1) { + FileInfo file_info; + if (os_get_file_info((char *)fname, &file_info)) { #ifdef UNIX /* print name of owner of the file */ - if (os_get_uname(st.st_uid, uname, B0_UNAME_SIZE) == OK) { + if (os_get_uname(file_info.stat.st_uid, uname, B0_UNAME_SIZE) == OK) { MSG_PUTS(_(" owned by: ")); msg_outtrans((char_u *)uname); MSG_PUTS(_(" dated: ")); } else #endif MSG_PUTS(_(" dated: ")); - x = st.st_mtime; /* Manx C can't do &st.st_mtime */ - p = ctime(&x); /* includes '\n' */ + x = file_info.stat.st_mtim.tv_sec; + p = ctime(&x); // includes '\n' if (p == NULL) MSG_PUTS("(invalid)\n"); else @@ -1850,7 +1848,6 @@ static int recov_file_names(char_u **names, char_u *path, int prepend_dot) void ml_sync_all(int check_file, int check_char) { buf_T *buf; - struct stat st; for (buf = firstbuf; buf != NULL; buf = buf->b_next) { if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL) @@ -1865,9 +1862,10 @@ void ml_sync_all(int check_file, int check_char) * If the original file does not exist anymore or has been changed * call ml_preserve() to get rid of all negative numbered blocks. */ - if (mch_stat((char *)buf->b_ffname, &st) == -1 - || st.st_mtime != buf->b_mtime_read - || st.st_size != buf->b_orig_size) { + FileInfo file_info; + if (!os_get_file_info((char *)buf->b_ffname, &file_info) + || file_info.stat.st_mtim.tv_sec != buf->b_mtime_read + || (off_t)file_info.stat.st_size != buf->b_orig_size) { ml_preserve(buf, FALSE); did_check_timestamps = FALSE; need_check_timestamps = TRUE; /* give message later */ @@ -3435,7 +3433,6 @@ attention_message ( char_u *fname /* swap file name */ ) { - struct stat st; time_t x, sx; char *p; @@ -3448,10 +3445,11 @@ attention_message ( MSG_PUTS(_("While opening file \"")); msg_outtrans(buf->b_fname); MSG_PUTS("\"\n"); - if (mch_stat((char *)buf->b_fname, &st) != -1) { + FileInfo file_info; + if (os_get_file_info((char *)buf->b_fname, &file_info)) { MSG_PUTS(_(" dated: ")); - x = st.st_mtime; /* Manx C can't do &st.st_mtime */ - p = ctime(&x); /* includes '\n' */ + x = file_info.stat.st_mtim.tv_sec; + p = ctime(&x); // includes '\n' if (p == NULL) MSG_PUTS("(invalid)\n"); else @@ -3559,21 +3557,13 @@ findswapname ( fname = NULL; break; } - /* - * check if the swapfile already exists - */ - if (!os_file_exists(fname)) { /* it does not exist */ -#ifdef HAVE_LSTAT - struct stat sb; - - /* - * Extra security check: When a swap file is a symbolic link, this - * is most likely a symlink attack. - */ - if (mch_lstat((char *)fname, &sb) < 0) -#else -#endif - break; + // check if the swapfile already exists + // Extra security check: When a swap file is a symbolic link, this + // is most likely a symlink attack. + FileInfo file_info; + bool file_or_link_found = os_get_file_info_link((char *)fname, &file_info); + if (!file_or_link_found) { + break; } /* @@ -3834,7 +3824,6 @@ fnamecmp_ino ( long ino_block0 ) { - struct stat st; ino_t ino_c = 0; /* ino of current file */ ino_t ino_s; /* ino of file from swap file */ char_u buf_c[MAXPATHL]; /* full path of fname_c */ @@ -3842,18 +3831,21 @@ fnamecmp_ino ( int retval_c; /* flag: buf_c valid */ int retval_s; /* flag: buf_s valid */ - if (mch_stat((char *)fname_c, &st) == 0) - ino_c = (ino_t)st.st_ino; + FileInfo file_info; + if (os_get_file_info((char *)fname_c, &file_info)) { + ino_c = (ino_t)file_info.stat.st_ino; + } /* * First we try to get the inode from the file name, because the inode in * the swap file may be outdated. If that fails (e.g. this path is not * valid on this machine), use the inode from block 0. */ - if (mch_stat((char *)fname_s, &st) == 0) - ino_s = (ino_t)st.st_ino; - else + if (os_get_file_info((char *)fname_s, &file_info)) { + ino_s = (ino_t)file_info.stat.st_ino; + } else { ino_s = (ino_t)ino_block0; + } if (ino_c && ino_s) return ino_c != ino_s; diff --git a/src/misc2.c b/src/misc2.c index c424dd8709..8d556db347 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -826,24 +826,6 @@ int vim_chdirfile(char_u *fname) } #endif -#if defined(STAT_IGNORES_SLASH) || defined(PROTO) -/* - * Check if "name" ends in a slash and is not a directory. - * Used for systems where stat() ignores a trailing slash on a file name. - * The Vim code assumes a trailing slash is only ignored for a directory. - */ -int illegal_slash(char *name) -{ - if (name[0] == NUL) - return FALSE; /* no file name is not illegal */ - if (name[strlen(name) - 1] != '/') - return FALSE; /* no trailing slash */ - if (os_isdir((char_u *)name)) - return FALSE; /* trailing slash for a directory */ - return TRUE; -} -#endif - /* * Change directory to "new_dir". If FEAT_SEARCHPATH is defined, search * 'cdpath' for relative directory names, otherwise just os_chdir(). diff --git a/src/os/fs.c b/src/os/fs.c index e03d06669d..89eb4c8691 100644 --- a/src/os/fs.c +++ b/src/os/fs.c @@ -189,6 +189,16 @@ int os_file_is_writable(const char *name) return 0; } +bool os_get_file_size(const char *name, off_t *size) +{ + uv_stat_t statbuf; + if (os_stat((char_u *)name, &statbuf) == OK) { + *size = statbuf.st_size; + return true; + } + return false; +} + int os_rename(const char_u *path, const char_u *new_path) { uv_fs_t request; @@ -227,3 +237,41 @@ int os_remove(const char *path) return result; } +bool os_get_file_info(const char *path, FileInfo *file_info) +{ + if (os_stat((char_u *)path, &(file_info->stat)) == OK) { + return true; + } + return false; +} + +bool os_get_file_info_link(const char *path, FileInfo *file_info) +{ + uv_fs_t request; + int result = uv_fs_lstat(uv_default_loop(), &request, path, NULL); + file_info->stat = request.statbuf; + uv_fs_req_cleanup(&request); + if (result == kLibuvSuccess) { + return true; + } + return false; +} + +bool os_get_file_info_fd(int file_descriptor, FileInfo *file_info) +{ + uv_fs_t request; + int result = uv_fs_fstat(uv_default_loop(), &request, file_descriptor, NULL); + file_info->stat = request.statbuf; + uv_fs_req_cleanup(&request); + if (result == kLibuvSuccess) { + return true; + } + return false; +} + +bool os_file_info_id_equal(FileInfo *file_info_1, FileInfo *file_info_2) +{ + return file_info_1->stat.st_ino == file_info_2->stat.st_ino + && file_info_1->stat.st_dev == file_info_2->stat.st_dev; +} + diff --git a/src/os/os.h b/src/os/os.h index 67cda28348..b30872f06d 100644 --- a/src/os/os.h +++ b/src/os/os.h @@ -58,6 +58,12 @@ bool os_file_is_readonly(const char *name); /// @return `2` for a directory which we have rights to write into. int os_file_is_writable(const char *name); +/// Get the size of a file in bytes. +/// +/// @param[out] size pointer to an off_t to put the size into. +/// @return `true` for success, `false` for failure. +bool os_get_file_size(const char *name, off_t *size); + /// Rename a file or directory. /// /// @return `OK` for success, `FAIL` for failure. @@ -105,4 +111,36 @@ char *os_get_user_directory(const char *name); /// @return OK on success, FAIL if an failure occured. int os_stat(const char_u *name, uv_stat_t *statbuf); +/// Struct which encapsulates stat information. +typedef struct { + // TODO(stefan991): make stat private + uv_stat_t stat; +} FileInfo; + +/// Get the file information for a given path +/// +/// @param file_descriptor File descriptor of the file. +/// @param[out] file_info Pointer to a FileInfo to put the information in. +/// @return `true` on sucess, `false` for failure. +bool os_get_file_info(const char *path, FileInfo *file_info); + +/// Get the file information for a given path without following links +/// +/// @param path Path to the file. +/// @param[out] file_info Pointer to a FileInfo to put the information in. +/// @return `true` on sucess, `false` for failure. +bool os_get_file_info_link(const char *path, FileInfo *file_info); + +/// Get the file information for a given file descriptor +/// +/// @param file_descriptor File descriptor of the file. +/// @param[out] file_info Pointer to a FileInfo to put the information in. +/// @return `true` on sucess, `false` for failure. +bool os_get_file_info_fd(int file_descriptor, FileInfo *file_info); + +/// Compare the inodes of two FileInfos +/// +/// @return `true` if the two FileInfos represent the same file. +bool os_file_info_id_equal(FileInfo *file_info_1, FileInfo *file_info_2); + #endif // NEOVIM_OS_OS_H diff --git a/src/os_unix_defs.h b/src/os_unix_defs.h index 5ca09a7584..96fc7ff32e 100644 --- a/src/os_unix_defs.h +++ b/src/os_unix_defs.h @@ -229,6 +229,7 @@ # define MAXPATHL 1024 #endif +// TODO(stefan991): remove macro #define CHECK_INODE /* used when checking if a swap file already exists for a file */ # ifndef DFLT_MAXMEM @@ -276,7 +277,6 @@ #endif #define HAVE_DUP /* have dup() */ -#define HAVE_ST_MODE /* have stat.st_mode */ /* We have three kinds of ACL support. */ #define HAVE_ACL (HAVE_POSIX_ACL || HAVE_SOLARIS_ACL || HAVE_AIX_ACL) diff --git a/src/path.c b/src/path.c index 93366c93ab..541e1e1724 100644 --- a/src/path.c +++ b/src/path.c @@ -1285,7 +1285,6 @@ void simplify_filename(char_u *filename) if (components > 0) { /* strip one preceding component */ int do_strip = FALSE; char_u saved_char; - struct stat st; /* Don't strip for an erroneous file name. */ if (!stripping_disabled) { @@ -1294,12 +1293,10 @@ void simplify_filename(char_u *filename) * link that refers to a non-existent file. */ saved_char = p[-1]; p[-1] = NUL; -#ifdef UNIX - if (mch_lstat((char *)filename, &st) < 0) -#else - if (mch_stat((char *)filename, &st) < 0) -#endif + FileInfo file_info; + if (!os_get_file_info_link((char *)filename, &file_info)) { do_strip = TRUE; + } p[-1] = saved_char; --p; @@ -1320,40 +1317,37 @@ void simplify_filename(char_u *filename) * components. */ saved_char = *tail; *tail = NUL; - if (mch_stat((char *)filename, &st) >= 0) + if (os_get_file_info((char *)filename, &file_info)) { do_strip = TRUE; + } else stripping_disabled = TRUE; *tail = saved_char; -#ifdef UNIX if (do_strip) { - struct stat new_st; - - /* On Unix, the check for the unstripped file name + /* The check for the unstripped file name * above works also for a symbolic link pointing to * a searchable directory. But then the parent of * the directory pointed to by the link must be the * same as the stripped file name. (The latter * exists in the file system since it is the * component's parent directory.) */ - if (p == start && relative) - (void)mch_stat(".", &new_st); - else { + FileInfo new_file_info; + if (p == start && relative) { + os_get_file_info(".", &new_file_info); + } else { saved_char = *p; *p = NUL; - (void)mch_stat((char *)filename, &new_st); + os_get_file_info((char *)filename, &new_file_info); *p = saved_char; } - if (new_st.st_ino != st.st_ino || - new_st.st_dev != st.st_dev) { + if (!os_file_info_id_equal(&file_info, &new_file_info)) { do_strip = FALSE; /* We don't disable stripping of later * components since the unstripped path name is * still valid. */ } } -#endif } } diff --git a/src/quickfix.c b/src/quickfix.c index 91ebe9795b..94b2b05501 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -2573,9 +2573,6 @@ static char_u *get_mef_name(void) char_u *name; static int start = -1; static int off = 0; -#ifdef HAVE_LSTAT - struct stat sb; -#endif if (*p_mef == NUL) { name = vim_tempname('e'); @@ -2602,13 +2599,12 @@ static char_u *get_mef_name(void) STRCPY(name, p_mef); sprintf((char *)name + (p - p_mef), "%d%d", start, off); STRCAT(name, p + 2); - if (!os_file_exists(name) -#ifdef HAVE_LSTAT - /* Don't accept a symbolic link, its a security risk. */ - && mch_lstat((char *)name, &sb) < 0 -#endif - ) + // Don't accept a symbolic link, its a security risk. + FileInfo file_info; + bool file_or_link_found = os_get_file_info_link((char *)name, &file_info); + if (!file_or_link_found) { break; + } free(name); } return name; diff --git a/src/spell.c b/src/spell.c index 5ee799ebf1..5392fe4a70 100644 --- a/src/spell.c +++ b/src/spell.c @@ -7768,7 +7768,6 @@ mkspell ( afffile_T *(afile[8]); int i; int len; - struct stat st; int error = FALSE; spellinfo_T spin; @@ -7832,7 +7831,7 @@ mkspell ( else { // Check for overwriting before doing things that may take a lot of // time. - if (!over_write && mch_stat((char *)wfname, &st) >= 0) { + if (!over_write && os_file_exists(wfname)) { EMSG(_(e_exists)); goto theend; } @@ -7888,7 +7887,7 @@ mkspell ( spin.si_region = 1 << i; vim_snprintf((char *)fname, MAXPATHL, "%s.aff", innames[i]); - if (mch_stat((char *)fname, &st) >= 0) { + if (os_file_exists(fname)) { // Read the .aff file. Will init "spin->si_conv" based on the // "SET" line. afile[i] = spell_read_aff(&spin, fname); @@ -1511,12 +1511,10 @@ line_read_in: * compute the first offset. */ if (state == TS_BINARY) { - /* Get the tag file size (don't use mch_fstat(), it's not - * portable). */ - if ((filesize = lseek(fileno(fp), - (off_t)0L, SEEK_END)) <= 0) + // Get the tag file size. + if ((filesize = lseek(fileno(fp), (off_t)0L, SEEK_END)) <= 0) { state = TS_LINEAR; - else { + } else { lseek(fileno(fp), (off_t)0L, SEEK_SET); /* Calculate the first read offset in the file. Start diff --git a/src/undo.c b/src/undo.c index a17ba50255..47ce8ee8da 100644 --- a/src/undo.c +++ b/src/undo.c @@ -675,7 +675,6 @@ char_u *u_get_undo_file_name(char_u *buf_ffname, int reading) char_u *undo_file_name = NULL; int dir_len; char_u *p; - struct stat st; char_u *ffname = buf_ffname; #ifdef HAVE_READLINK char_u fname_buf[MAXPATHL]; @@ -723,7 +722,7 @@ char_u *u_get_undo_file_name(char_u *buf_ffname, int reading) // When reading check if the file exists. if (undo_file_name != NULL && - (!reading || mch_stat((char *)undo_file_name, &st) >= 0)) { + (!reading || os_file_exists(undo_file_name))) { break; } free(undo_file_name); @@ -1107,11 +1106,6 @@ void u_write_undo(char_u *name, int forceit, buf_T *buf, char_u *hash) FILE *fp = NULL; int perm; int write_ok = FALSE; -#ifdef UNIX - int st_old_valid = FALSE; - struct stat st_old; - struct stat st_new; -#endif int do_crypt = FALSE; if (name == NULL) { @@ -1135,16 +1129,10 @@ void u_write_undo(char_u *name, int forceit, buf_T *buf, char_u *hash) */ perm = 0600; if (buf->b_ffname != NULL) { -#ifdef UNIX - if (mch_stat((char *)buf->b_ffname, &st_old) >= 0) { - perm = st_old.st_mode; - st_old_valid = TRUE; - } -#else perm = os_getperm(buf->b_ffname); - if (perm < 0) + if (perm < 0) { perm = 0600; -#endif + } } /* strip any s-bit */ @@ -1223,14 +1211,17 @@ void u_write_undo(char_u *name, int forceit, buf_T *buf, char_u *hash) * this fails, set the protection bits for the group same as the * protection bits for others. */ - if (st_old_valid - && mch_stat((char *)file_name, &st_new) >= 0 - && st_new.st_gid != st_old.st_gid + FileInfo file_info_old; + FileInfo file_info_new; + if (os_get_file_info((char *)buf->b_ffname, &file_info_old) + && os_get_file_info((char *)file_name, &file_info_new) + && file_info_old.stat.st_gid != file_info_new.stat.st_gid # ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */ - && fchown(fd, (uid_t)-1, st_old.st_gid) != 0 + && fchown(fd, (uid_t)-1, file_info_old.stat.st_gid) != 0 # endif - ) + ) { os_setperm(file_name, (perm & 0707) | ((perm & 07) << 3)); + } # ifdef HAVE_SELINUX if (buf->b_ffname != NULL) mch_copy_sec(buf->b_ffname, file_name); @@ -1351,10 +1342,6 @@ void u_read_undo(char_u *name, char_u *hash, char_u *orig_name) #ifdef U_DEBUG int *uhp_table_used; #endif -#ifdef UNIX - struct stat st_orig; - struct stat st_undo; -#endif int do_decrypt = FALSE; if (name == NULL) { @@ -1365,10 +1352,12 @@ void u_read_undo(char_u *name, char_u *hash, char_u *orig_name) #ifdef UNIX /* For safety we only read an undo file if the owner is equal to the * owner of the text file or equal to the current user. */ - if (mch_stat((char *)orig_name, &st_orig) >= 0 - && mch_stat((char *)file_name, &st_undo) >= 0 - && st_orig.st_uid != st_undo.st_uid - && st_undo.st_uid != getuid()) { + FileInfo file_info_orig; + FileInfo file_info_undo; + if (os_get_file_info((char *)orig_name, &file_info_orig) + && os_get_file_info((char *)file_name, &file_info_undo) + && file_info_orig.stat.st_uid != file_info_undo.stat.st_uid + && file_info_undo.stat.st_uid != getuid()) { if (p_verbose > 0) { verbose_enter(); smsg((char_u *)_("Not reading undo file, owner differs: %s"), diff --git a/test/unit/os/fs.moon b/test/unit/os/fs.moon index c1ab4bd78a..b11e2515bd 100644 --- a/test/unit/os/fs.moon +++ b/test/unit/os/fs.moon @@ -14,6 +14,8 @@ describe 'fs function', -> setup -> lfs.mkdir 'unit-test-directory' (io.open 'unit-test-directory/test.file', 'w').close! + (io.open 'unit-test-directory/test_2.file', 'w').close! + lfs.link 'test.file', 'unit-test-directory/test_link.file', true -- Since the tests are executed, they are called by an executable. We use -- that executable for several asserts. @@ -25,6 +27,8 @@ describe 'fs function', -> teardown -> os.remove 'unit-test-directory/test.file' + os.remove 'unit-test-directory/test_2.file' + os.remove 'unit-test-directory/test_link.file' lfs.rmdir 'unit-test-directory' describe 'os_dirname', -> @@ -213,6 +217,12 @@ describe 'fs function', -> eq 2, os_file_is_writable 'unit-test-directory' describe 'file operations', -> + setup -> + (io.open 'unit-test-directory/test_remove.file', 'w').close! + + teardown -> + os.remove 'unit-test-directory/test_remove.file' + os_file_exists = (filename) -> fs.os_file_exists (to_cstr filename) @@ -261,9 +271,9 @@ describe 'fs function', -> neq 0, (os_remove 'non-existing-file') it 'removes the given file and returns 0', -> - eq true, (os_file_exists 'unit-test-directory/test.file') - eq 0, (os_remove 'unit-test-directory/test.file') - eq false, (os_file_exists 'unit-test-directory/test.file') + eq true, (os_file_exists 'unit-test-directory/test_remove.file') + eq 0, (os_remove 'unit-test-directory/test_remove.file') + eq false, (os_file_exists 'unit-test-directory/test_remove.file') describe 'folder operations', -> os_mkdir = (path, mode) -> @@ -293,3 +303,92 @@ describe 'fs function', -> eq 0, (os_rmdir 'unit-test-directory/new-dir', mode) eq false, (os_isdir 'unit-test-directory/new-dir') + describe 'FileInfo', -> + + file_info_new = () -> + file_info = ffi.new 'FileInfo[1]' + file_info[0].stat.st_ino = 0 + file_info[0].stat.st_dev = 0 + file_info + + is_file_info_filled = (file_info) -> + file_info[0].stat.st_ino > 0 and file_info[0].stat.st_dev > 0 + + describe 'os_get_file_info', -> + it 'returns false if given an non-existing file', -> + file_info = file_info_new! + assert.is_false (fs.os_get_file_info '/non-existent', file_info) + + it 'returns true if given an existing file and fills file_info', -> + file_info = file_info_new! + path = 'unit-test-directory/test.file' + assert.is_true (fs.os_get_file_info path, file_info) + assert.is_true (is_file_info_filled file_info) + + it 'returns the file info of the linked file, not the link', -> + file_info = file_info_new! + path = 'unit-test-directory/test_link.file' + assert.is_true (fs.os_get_file_info path, file_info) + assert.is_true (is_file_info_filled file_info) + mode = tonumber file_info[0].stat.st_mode + eq ffi.C.kS_IFREG, (bit.band mode, ffi.C.kS_IFMT) + + describe 'os_get_file_info_link', -> + it 'returns false if given an non-existing file', -> + file_info = file_info_new! + assert.is_false (fs.os_get_file_info_link '/non-existent', file_info) + + it 'returns true if given an existing file and fills file_info', -> + file_info = file_info_new! + path = 'unit-test-directory/test.file' + assert.is_true (fs.os_get_file_info_link path, file_info) + assert.is_true (is_file_info_filled file_info) + + it 'returns the file info of the link, not the linked file', -> + file_info = file_info_new! + path = 'unit-test-directory/test_link.file' + assert.is_true (fs.os_get_file_info_link path, file_info) + assert.is_true (is_file_info_filled file_info) + mode = tonumber file_info[0].stat.st_mode + eq ffi.C.kS_IFLNK, (bit.band mode, ffi.C.kS_IFMT) + + describe 'os_get_file_info_fd', -> + it 'returns false if given an invalid file descriptor', -> + file_info = file_info_new! + assert.is_false (fs.os_get_file_info_fd -1, file_info) + + it 'returns true if given an file descriptor and fills file_info', -> + file_info = file_info_new! + path = 'unit-test-directory/test.file' + fd = ffi.C.open path, 0 + assert.is_true (fs.os_get_file_info_fd fd, file_info) + assert.is_true (is_file_info_filled file_info) + ffi.C.close fd + + describe 'os_file_info_id_equal', -> + it 'returns false if file infos represent different files', -> + file_info_1 = file_info_new! + file_info_2 = file_info_new! + path_1 = 'unit-test-directory/test.file' + path_2 = 'unit-test-directory/test_2.file' + assert.is_true (fs.os_get_file_info path_1, file_info_1) + assert.is_true (fs.os_get_file_info path_2, file_info_2) + assert.is_false (fs.os_file_info_id_equal file_info_1, file_info_2) + + it 'returns true if file infos represent the same file', -> + file_info_1 = file_info_new! + file_info_2 = file_info_new! + path = 'unit-test-directory/test.file' + assert.is_true (fs.os_get_file_info path, file_info_1) + assert.is_true (fs.os_get_file_info path, file_info_2) + assert.is_true (fs.os_file_info_id_equal file_info_1, file_info_2) + + it 'returns true if file infos represent the same file (symlink)', -> + file_info_1 = file_info_new! + file_info_2 = file_info_new! + path_1 = 'unit-test-directory/test.file' + path_2 = 'unit-test-directory/test_link.file' + assert.is_true (fs.os_get_file_info path_1, file_info_1) + assert.is_true (fs.os_get_file_info path_2, file_info_2) + assert.is_true (fs.os_file_info_id_equal file_info_1, file_info_2) + |