/* File IO for GNU Emacs.
-Copyright (C) 1985-1988, 1993-2013 Free Software Foundation, Inc.
+Copyright (C) 1985-1988, 1993-2015 Free Software Foundation, Inc.
This file is part of GNU Emacs.
/* True during writing of auto-save files. */
static bool auto_saving;
+/* Emacs's real umask. */
+static mode_t realmask;
+
/* Nonzero umask during creation of auto-save directories. */
static mode_t auto_saving_dir_umask;
static Lisp_Object Qsubstitute_env_in_file_name;
-#ifdef WINDOWSNT
-#endif
-
Lisp_Object Qfile_error, Qfile_notify_error;
static Lisp_Object Qfile_already_exists, Qfile_date_error;
static Lisp_Object Qexcl;
static Lisp_Object Qset_file_acl;
static Lisp_Object Qfile_newer_than_file_p;
Lisp_Object Qinsert_file_contents;
-static Lisp_Object Qchoose_write_coding_system;
Lisp_Object Qwrite_region;
static Lisp_Object Qverify_visited_file_modtime;
static Lisp_Object Qset_visited_file_modtime;
make_temp_name (Lisp_Object prefix, bool base64_p)
{
Lisp_Object val, encoded_prefix;
- int len;
+ ptrdiff_t len;
printmax_t pid;
char *p, *data;
char pidbuf[INT_BUFSIZE_BOUND (printmax_t)];
DEFUN ("make-temp-name", Fmake_temp_name, Smake_temp_name, 1, 1, 0,
doc: /* Generate temporary file name (string) starting with PREFIX (a string).
-The Emacs process number forms part of the result,
-so there is no danger of generating a name being used by another process.
+The Emacs process number forms part of the result, so there is no
+danger of generating a name being used by another Emacs process
+\(so long as only a single host can access the containing directory...).
-In addition, this function makes an attempt to choose a name
-which has no existing file. To make this work,
-PREFIX should be an absolute file name.
+This function tries to choose a name that has no existing file.
+For this to work, PREFIX should be an absolute file name.
There is a race condition between calling `make-temp-name' and creating the
-file which opens all kinds of security holes. For that reason, you should
-probably use `make-temp-file' instead, except in three circumstances:
-
-* If you are creating the file in the user's home directory.
-* If you are creating a directory rather than an ordinary file.
-* If you are taking special precautions as `make-temp-file' does. */)
+file, which opens all kinds of security holes. For that reason, you should
+normally use `make-temp-file' instead. */)
(Lisp_Object prefix)
{
return make_temp_name (prefix, 0);
nm++;
/* Discard any previous drive specifier if nm is now in UNC format. */
- if (IS_DIRECTORY_SEP (nm[0]) && IS_DIRECTORY_SEP (nm[1]))
- {
- drive = 0;
- }
+ if (IS_DIRECTORY_SEP (nm[0]) && IS_DIRECTORY_SEP (nm[1])
+ && !IS_DIRECTORY_SEP (nm[2]))
+ drive = 0;
#endif /* WINDOWSNT */
#endif /* DOS_NT */
char newdir_utf8[MAX_UTF8_PATH];
filename_from_ansi (newdir, newdir_utf8);
- tem = build_string (newdir_utf8);
+ tem = make_unibyte_string (newdir_utf8, strlen (newdir_utf8));
}
else
-#else
- tem = build_string (newdir);
#endif
+ tem = build_string (newdir);
if (multibyte && !STRING_MULTIBYTE (tem))
{
hdir = DECODE_FILE (tem);
/* `getpwnam' may return a unibyte string, which will
bite us since we expect the directory to be
multibyte. */
- tem = build_string (newdir);
+ tem = make_unibyte_string (newdir, strlen (newdir));
if (multibyte && !STRING_MULTIBYTE (tem))
{
hdir = DECODE_FILE (tem);
&& !IS_DIRECTORY_SEP (nm[0])
#endif
#ifdef WINDOWSNT
- && !(IS_DIRECTORY_SEP (nm[0]) && IS_DIRECTORY_SEP (nm[1]))
+ && !(IS_DIRECTORY_SEP (nm[0]) && IS_DIRECTORY_SEP (nm[1])
+ && !IS_DIRECTORY_SEP (nm[2]))
#endif
&& !newdir)
{
&& IS_DEVICE_SEP (newdir[1]) && IS_DIRECTORY_SEP (newdir[2]))
#ifdef WINDOWSNT
/* Detect Windows file names in UNC format. */
- && ! (IS_DIRECTORY_SEP (newdir[0]) && IS_DIRECTORY_SEP (newdir[1]))
+ && ! (IS_DIRECTORY_SEP (newdir[0]) && IS_DIRECTORY_SEP (newdir[1])
+ && !IS_DIRECTORY_SEP (newdir[2]))
#endif
)
{
if (IS_DIRECTORY_SEP (nm[0]) && collapse_newdir)
{
#ifdef WINDOWSNT
- if (IS_DIRECTORY_SEP (newdir[0]) && IS_DIRECTORY_SEP (newdir[1]))
+ if (IS_DIRECTORY_SEP (newdir[0]) && IS_DIRECTORY_SEP (newdir[1])
+ && !IS_DIRECTORY_SEP (newdir[2]))
{
char *adir = strcpy (alloca (strlen (newdir) + 1), newdir);
char *p = adir + 2;
}
\f
/* Signal an error if the file ABSNAME already exists.
- If INTERACTIVE, ask the user whether to proceed,
- and bypass the error if the user says to go ahead.
+ If KNOWN_TO_EXIST, the file is known to exist.
QUERYSTRING is a name for the action that is being considered
to alter the file.
-
- *STATPTR is used to store the stat information if the file exists.
- If the file does not exist, STATPTR->st_mode is set to 0.
- If STATPTR is null, we don't store into it.
-
+ If INTERACTIVE, ask the user whether to proceed,
+ and bypass the error if the user says to go ahead.
If QUICK, ask for y or n, not yes or no. */
static void
-barf_or_query_if_file_exists (Lisp_Object absname, const char *querystring,
- bool interactive, struct stat *statptr,
+barf_or_query_if_file_exists (Lisp_Object absname, bool known_to_exist,
+ const char *querystring, bool interactive,
bool quick)
{
Lisp_Object tem, encoded_filename;
encoded_filename = ENCODE_FILE (absname);
- /* `stat' is a good way to tell whether the file exists,
- regardless of what access permissions it has. */
- if (lstat (SSDATA (encoded_filename), &statbuf) >= 0)
+ if (! known_to_exist && lstat (SSDATA (encoded_filename), &statbuf) == 0)
{
if (S_ISDIR (statbuf.st_mode))
xsignal2 (Qfile_error,
build_string ("File is a directory"), absname);
+ known_to_exist = true;
+ }
+ if (known_to_exist)
+ {
if (! interactive)
xsignal2 (Qfile_already_exists,
build_string ("File already exists"), absname);
if (NILP (tem))
xsignal2 (Qfile_already_exists,
build_string ("File already exists"), absname);
- if (statptr)
- *statptr = statbuf;
}
- else
- {
- if (statptr)
- statptr->st_mode = 0;
- }
- return;
}
DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6,
If PRESERVE-UID-GID is non-nil, we try to transfer the
uid and gid of FILE to NEWNAME.
-If PRESERVE-EXTENDED-ATTRIBUTES is non-nil, we try to copy additional
-attributes of FILE to NEWNAME, such as its SELinux context and ACL
-entries (depending on how Emacs was built). */)
- (Lisp_Object file, Lisp_Object newname, Lisp_Object ok_if_already_exists, Lisp_Object keep_time, Lisp_Object preserve_uid_gid, Lisp_Object preserve_extended_attributes)
+If PRESERVE-PERMISSIONS is non-nil, copy permissions of FILE to NEWNAME;
+this includes the file modes, along with ACL entries and SELinux
+context if present. Otherwise, if NEWNAME is created its file
+permission bits are those of FILE, masked by the default file
+permissions. */)
+ (Lisp_Object file, Lisp_Object newname, Lisp_Object ok_if_already_exists,
+ Lisp_Object keep_time, Lisp_Object preserve_uid_gid,
+ Lisp_Object preserve_permissions)
{
- int ifd, ofd;
- int n;
- char buf[16 * 1024];
- struct stat st, out_st;
Lisp_Object handler;
struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
ptrdiff_t count = SPECPDL_INDEX ();
#endif
#ifdef WINDOWSNT
int result;
+#else
+ bool already_exists = false;
+ mode_t new_mask;
+ int ifd, ofd;
+ int n;
+ char buf[16 * 1024];
+ struct stat st;
#endif
encoded_file = encoded_newname = Qnil;
if (!NILP (handler))
RETURN_UNGCPRO (call7 (handler, Qcopy_file, file, newname,
ok_if_already_exists, keep_time, preserve_uid_gid,
- preserve_extended_attributes));
+ preserve_permissions));
encoded_file = ENCODE_FILE (file);
encoded_newname = ENCODE_FILE (newname);
+#ifdef WINDOWSNT
if (NILP (ok_if_already_exists)
|| INTEGERP (ok_if_already_exists))
- barf_or_query_if_file_exists (newname, "copy to it",
- INTEGERP (ok_if_already_exists), &out_st, 0);
- else if (stat (SSDATA (encoded_newname), &out_st) < 0)
- out_st.st_mode = 0;
+ barf_or_query_if_file_exists (newname, false, "copy to it",
+ INTEGERP (ok_if_already_exists), false);
-#ifdef WINDOWSNT
result = w32_copy_file (SSDATA (encoded_file), SSDATA (encoded_newname),
!NILP (keep_time), !NILP (preserve_uid_gid),
- !NILP (preserve_extended_attributes));
+ !NILP (preserve_permissions));
switch (result)
{
case -1:
if (fstat (ifd, &st) != 0)
report_file_error ("Input file status", file);
- if (!NILP (preserve_extended_attributes))
+ if (!NILP (preserve_permissions))
{
#if HAVE_LIBSELINUX
if (is_selinux_enabled ())
#endif
}
- if (out_st.st_mode != 0
- && st.st_dev == out_st.st_dev && st.st_ino == out_st.st_ino)
- report_file_errno ("Input and output files are the same",
- list2 (file, newname), 0);
-
/* We can copy only regular files. */
if (!S_ISREG (st.st_mode))
report_file_errno ("Non-regular file", file,
S_ISDIR (st.st_mode) ? EISDIR : EINVAL);
- {
#ifndef MSDOS
- int new_mask = st.st_mode & (!NILP (preserve_uid_gid) ? 0600 : 0666);
+ new_mask = st.st_mode & (!NILP (preserve_uid_gid) ? 0700 : 0777);
#else
- int new_mask = S_IREAD | S_IWRITE;
+ new_mask = S_IREAD | S_IWRITE;
#endif
- ofd = emacs_open (SSDATA (encoded_newname),
- (O_WRONLY | O_TRUNC | O_CREAT
- | (NILP (ok_if_already_exists) ? O_EXCL : 0)),
- new_mask);
- }
+
+ ofd = emacs_open (SSDATA (encoded_newname), O_WRONLY | O_CREAT | O_EXCL,
+ new_mask);
+ if (ofd < 0 && errno == EEXIST)
+ {
+ if (NILP (ok_if_already_exists) || INTEGERP (ok_if_already_exists))
+ barf_or_query_if_file_exists (newname, true, "copy to it",
+ INTEGERP (ok_if_already_exists), false);
+ already_exists = true;
+ ofd = emacs_open (SSDATA (encoded_newname), O_WRONLY, 0);
+ }
if (ofd < 0)
report_file_error ("Opening output file", newname);
record_unwind_protect_int (close_file_unwind, ofd);
+ if (already_exists)
+ {
+ struct stat out_st;
+ if (fstat (ofd, &out_st) != 0)
+ report_file_error ("Output file status", newname);
+ if (st.st_dev == out_st.st_dev && st.st_ino == out_st.st_ino)
+ report_file_errno ("Input and output files are the same",
+ list2 (file, newname), 0);
+ if (ftruncate (ofd, 0) != 0)
+ report_file_error ("Truncating output file", newname);
+ }
+
immediate_quit = 1;
QUIT;
while ((n = emacs_read (ifd, buf, sizeof buf)) > 0)
/* Preserve the original file permissions, and if requested, also its
owner and group. */
{
- mode_t mode_mask = 07777;
+ mode_t preserved_permissions = st.st_mode & 07777;
+ mode_t default_permissions = st.st_mode & 0777 & ~realmask;
if (!NILP (preserve_uid_gid))
{
/* Attempt to change owner and group. If that doesn't work
attempt to change just the group, as that is sometimes allowed.
Adjust the mode mask to eliminate setuid or setgid bits
- that are inappropriate if the owner and group are wrong. */
+ or group permissions bits that are inappropriate if the
+ owner or group are wrong. */
if (fchown (ofd, st.st_uid, st.st_gid) != 0)
{
- mode_mask &= ~06000;
if (fchown (ofd, -1, st.st_gid) == 0)
- mode_mask |= 02000;
+ preserved_permissions &= ~04000;
+ else
+ {
+ preserved_permissions &= ~06000;
+
+ /* Copy the other bits to the group bits, since the
+ group is wrong. */
+ preserved_permissions &= ~070;
+ preserved_permissions |= (preserved_permissions & 7) << 3;
+ default_permissions &= ~070;
+ default_permissions |= (default_permissions & 7) << 3;
+ }
}
}
- switch (!NILP (preserve_extended_attributes)
+ switch (!NILP (preserve_permissions)
? qcopy_acl (SSDATA (encoded_file), ifd,
SSDATA (encoded_newname), ofd,
- st.st_mode & mode_mask)
- : fchmod (ofd, st.st_mode & mode_mask))
+ preserved_permissions)
+ : (already_exists
+ || (new_mask & ~realmask) == default_permissions)
+ ? 0
+ : fchmod (ofd, default_permissions))
{
case -2: report_file_error ("Copying permissions from", file);
case -1: report_file_error ("Copying permissions to", newname);
{
struct timespec atime = get_stat_atime (&st);
struct timespec mtime = get_stat_mtime (&st);
- if (set_file_times (ofd, SSDATA (encoded_newname), atime, mtime))
+ if (set_file_times (ofd, SSDATA (encoded_newname), atime, mtime) != 0)
xsignal2 (Qfile_date_error,
build_string ("Cannot set file date"), newname);
}
#endif
if (NILP (ok_if_already_exists)
|| INTEGERP (ok_if_already_exists))
- barf_or_query_if_file_exists (newname, "rename to it",
- INTEGERP (ok_if_already_exists), 0, 0);
+ barf_or_query_if_file_exists (newname, false, "rename to it",
+ INTEGERP (ok_if_already_exists), false);
if (rename (SSDATA (encoded_file), SSDATA (encoded_newname)) < 0)
{
int rename_errno = errno;
if (NILP (ok_if_already_exists)
|| INTEGERP (ok_if_already_exists))
- barf_or_query_if_file_exists (newname, "make it a new name",
- INTEGERP (ok_if_already_exists), 0, 0);
+ barf_or_query_if_file_exists (newname, false, "make it a new name",
+ INTEGERP (ok_if_already_exists), false);
unlink (SSDATA (newname));
if (link (SSDATA (encoded_file), SSDATA (encoded_newname)) < 0)
if (NILP (ok_if_already_exists)
|| INTEGERP (ok_if_already_exists))
- barf_or_query_if_file_exists (linkname, "make it a link",
- INTEGERP (ok_if_already_exists), 0, 0);
+ barf_or_query_if_file_exists (linkname, false, "make it a link",
+ INTEGERP (ok_if_already_exists), false);
if (symlink (SSDATA (encoded_filename), SSDATA (encoded_linkname)) < 0)
{
/* If we didn't complain already, silently delete existing file. */
DEFUN ("file-executable-p", Ffile_executable_p, Sfile_executable_p, 1, 1, 0,
doc: /* Return t if FILENAME can be executed by you.
-For a directory, this means you can access files in that directory. */)
+For a directory, this means you can access files in that directory.
+\(It is generally better to use `file-accessible-directory-p' for that
+purpose, though.) */)
(Lisp_Object filename)
{
Lisp_Object absname;
The value is the link target, as a string.
Otherwise it returns nil.
-This function returns t when given the name of a symlink that
-points to a nonexistent file. */)
+This function does not check whether the link target exists. */)
(Lisp_Object filename)
{
Lisp_Object handler;
acl_t acl;
Lisp_Object acl_string;
char *str;
+# ifndef HAVE_ACL_TYPE_EXTENDED
+ acl_type_t ACL_TYPE_EXTENDED = ACL_TYPE_ACCESS;
+# endif
#endif
absname = expand_and_dir_to_file (filename,
#ifdef HAVE_ACL_SET_FILE
absname = ENCODE_FILE (absname);
- acl = acl_get_file (SSDATA (absname), ACL_TYPE_ACCESS);
+ acl = acl_get_file (SSDATA (absname), ACL_TYPE_EXTENDED);
if (acl == NULL)
return Qnil;
This setting is inherited by subprocesses. */)
(Lisp_Object mode)
{
+ mode_t oldrealmask, oldumask, newumask;
CHECK_NUMBER (mode);
+ oldrealmask = realmask;
+ newumask = ~ XINT (mode) & 0777;
- umask ((~ XINT (mode)) & 0777);
+ block_input ();
+ realmask = newumask;
+ oldumask = umask (newumask);
+ unblock_input ();
+ eassert (oldumask == oldrealmask);
return Qnil;
}
The value is an integer. */)
(void)
{
- mode_t realmask;
Lisp_Object value;
-
- block_input ();
- realmask = umask (0);
- umask (realmask);
- unblock_input ();
-
XSETINT (value, (~ realmask) & 0777);
return value;
}
encoded_absname = ENCODE_FILE (absname);
{
- if (set_file_times (-1, SSDATA (encoded_absname), t, t))
+ if (set_file_times (-1, SSDATA (encoded_absname), t, t) != 0)
{
#ifdef MSDOS
/* Setting times on a directory always fails. */
&& SAVE_MODIFF >= MODIFF)
we_locked_file = 1;
#endif /* CLASH_DETECTION */
- prepare_to_modify_buffer (GPT, GPT, NULL);
+ prepare_to_modify_buffer (PT, PT, NULL);
}
move_gap_both (PT, PT_BYTE);
coding_system = CODING_ID_NAME (coding.id);
}
else if (inserted > 0)
- adjust_after_insert (PT, PT_BYTE, PT + inserted, PT_BYTE + inserted,
- inserted);
+ {
+ invalidate_buffer_caches (current_buffer, PT, PT + inserted);
+ adjust_after_insert (PT, PT_BYTE, PT + inserted, PT_BYTE + inserted,
+ inserted);
+ }
/* Call after-change hooks for the inserted text, aside from the case
of normal visiting (not with REPLACE), which is done in a new buffer
/* We made a lot of deletions and insertions above, so invalidate
the newline cache for the entire region of the inserted
characters. */
- if (current_buffer->newline_cache)
+ if (current_buffer->base_buffer && current_buffer->base_buffer->newline_cache)
+ invalidate_region_cache (current_buffer->base_buffer,
+ current_buffer->base_buffer->newline_cache,
+ PT - BEG, Z - PT - inserted);
+ else if (current_buffer->newline_cache)
invalidate_region_cache (current_buffer,
current_buffer->newline_cache,
PT - BEG, Z - PT - inserted);
/* Decide the coding-system to encode the data with. */
-DEFUN ("choose-write-coding-system", Fchoose_write_coding_system,
- Schoose_write_coding_system, 3, 6, 0,
- doc: /* Choose the coding system for writing a file.
-Arguments are as for `write-region'.
-This function is for internal use only. It may prompt the user. */ )
- (Lisp_Object start, Lisp_Object end, Lisp_Object filename,
- Lisp_Object append, Lisp_Object visit, Lisp_Object lockname)
+static Lisp_Object
+choose_write_coding_system (Lisp_Object start, Lisp_Object end, Lisp_Object filename,
+ Lisp_Object append, Lisp_Object visit, Lisp_Object lockname,
+ struct coding_system *coding)
{
Lisp_Object val;
Lisp_Object eol_parent = Qnil;
- /* Mimic write-region behavior. */
- if (NILP (start))
- {
- XSETFASTINT (start, BEGV);
- XSETFASTINT (end, ZV);
- }
-
if (auto_saving
&& NILP (Fstring_equal (BVAR (current_buffer, filename),
BVAR (current_buffer, auto_save_file_name))))
}
val = coding_inherit_eol_type (val, eol_parent);
+ setup_coding_system (val, coding);
+
+ if (!STRINGP (start) && !NILP (BVAR (current_buffer, selective_display)))
+ coding->mode |= CODING_MODE_SELECTIVE_DISPLAY;
return val;
}
filename = Fexpand_file_name (filename, Qnil);
if (!NILP (mustbenew) && !EQ (mustbenew, Qexcl))
- barf_or_query_if_file_exists (filename, "overwrite", 1, 0, 1);
+ barf_or_query_if_file_exists (filename, false, "overwrite", true, true);
if (STRINGP (visit))
visit_file = Fexpand_file_name (visit, Qnil);
We used to make this choice before calling build_annotations, but that
leads to problems when a write-annotate-function takes care of
unsavable chars (as was the case with X-Symbol). */
- Vlast_coding_system_used =
- Fchoose_write_coding_system (start, end, filename,
- append, visit, lockname);
-
- setup_coding_system (Vlast_coding_system_used, &coding);
-
- if (!STRINGP (start) && !NILP (BVAR (current_buffer, selective_display)))
- coding.mode |= CODING_MODE_SELECTIVE_DISPLAY;
+ Vlast_coding_system_used
+ = choose_write_coding_system (start, end, filename,
+ append, visit, lockname, &coding);
#ifdef CLASH_DETECTION
if (open_and_close_file && !auto_saving)
doc: /* Return t if (car A) is numerically less than (car B). */)
(Lisp_Object a, Lisp_Object b)
{
- Lisp_Object args[2] = { Fcar (a), Fcar (b), };
+ Lisp_Object args[2];
+ args[0] = Fcar (a);
+ args[1] = Fcar (b);
return Flss (2, args);
}
void
init_fileio (void)
{
+ realmask = umask (0);
+ umask (realmask);
+
valid_timestamp_file_system = 0;
/* fsync can be a significant performance hit. Often it doesn't
DEFSYM (Qset_file_acl, "set-file-acl");
DEFSYM (Qfile_newer_than_file_p, "file-newer-than-file-p");
DEFSYM (Qinsert_file_contents, "insert-file-contents");
- DEFSYM (Qchoose_write_coding_system, "choose-write-coding-system");
DEFSYM (Qwrite_region, "write-region");
DEFSYM (Qverify_visited_file_modtime, "verify-visited-file-modtime");
DEFSYM (Qset_visited_file_modtime, "set-visited-file-modtime");
doc: /* Non-nil means don't call fsync in `write-region'.
This variable affects calls to `write-region' as well as save commands.
Setting this to nil may avoid data loss if the system loses power or
-the operating system crashes. */);
+the operating system crashes. By default, it is non-nil in batch mode. */);
write_region_inhibit_fsync = 0; /* See also `init_fileio' above. */
DEFVAR_BOOL ("delete-by-moving-to-trash", delete_by_moving_to_trash,
defsubr (&Sdefault_file_modes);
defsubr (&Sfile_newer_than_file_p);
defsubr (&Sinsert_file_contents);
- defsubr (&Schoose_write_coding_system);
defsubr (&Swrite_region);
defsubr (&Scar_less_than_car);
defsubr (&Sverify_visited_file_modtime);