TEST(UNISTD_TEST, lockf_partial_with_child) { constexpr off64_t file_size = 32*1024LL; TemporaryFile tf; ASSERT_EQ(0, ftruncate(tf.fd, file_size)); // Lock the first half of the file. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_LOCK, file_size/2)); // Fork a child process. pid_t pid = fork(); ASSERT_NE(-1, pid); if (pid == 0) { // Check that the child can lock the other half. ASSERT_EQ(file_size/2, lseek64(tf.fd, file_size/2, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_TLOCK, file_size/2)); // Check that the child cannot lock the first half. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(-1, lockf64(tf.fd, F_TEST, file_size/2)); ASSERT_EQ(EACCES, errno); // Check also that it reports itself as locked. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(-1, lockf64(tf.fd, F_TEST, file_size/2)); ASSERT_EQ(EACCES, errno); _exit(0); } AssertChildExited(pid, 0); // The second half was locked by the child, but the lock disappeared // when the process exited, so check it can be locked now. ASSERT_EQ(file_size/2, lseek64(tf.fd, file_size/2, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_TLOCK, file_size/2)); }
TEST(UNISTD_TEST, lockf_with_child) { constexpr off64_t file_size = 32*1024LL; TemporaryFile tf; ASSERT_EQ(0, ftruncate(tf.fd, file_size)); // Lock everything. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_LOCK, file_size)); // Fork a child process pid_t pid = fork(); ASSERT_NE(-1, pid); if (pid == 0) { // Check that the child cannot lock the file. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(-1, lockf64(tf.fd, F_TLOCK, file_size)); ASSERT_EQ(EAGAIN, errno); // Check also that it reports itself as locked. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(-1, lockf64(tf.fd, F_TEST, file_size)); ASSERT_EQ(EACCES, errno); _exit(0); } AssertChildExited(pid, 0); }
TEST(UNISTD_TEST, lockf_negative) { constexpr off64_t file_size = 32*1024LL; TemporaryFile tf; ASSERT_EQ(0, ftruncate(tf.fd, file_size)); // Lock everything, but specifying the range in reverse. ASSERT_EQ(file_size, lseek64(tf.fd, file_size, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_LOCK, -file_size)); // Check that it's locked. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_TEST, file_size)); }
xbShort xbXBase::LockFile( int fn, xbShort LockType, xbOffT lockLen ) { xbShort cmd, rc; xbShort tries = 0; /* convert cross platform xbase lock type to unix lock type */ if( LockType == XB_UNLOCK ) cmd = F_ULOCK; else if( LockType == XB_LOCK || LockType == XB_LOCK_HOLD ) cmd = F_TLOCK; else return XB_INVALID_LOCK_OPTION; /* do the actual lock */ do{ #ifdef _LARGEFILE64_SOURCE rc = lockf64( fn, cmd, lockLen ); #else rc = lockf( fn, cmd, lockLen ); #endif if( rc == -1 && errno != EINTR ){ tries++; sleep(1); } } while( rc == -1 && tries < GetLockRetryCount()); if( rc ) return XB_LOCK_FAILED; return XB_NO_ERROR; }
TEST(UNISTD_TEST, lockf_zero) { constexpr off64_t file_size = 32*1024LL; TemporaryFile tf; ASSERT_EQ(0, ftruncate(tf.fd, file_size)); // Lock everything by specifying a size of 0 (meaning "to the end, even if it changes"). ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_LOCK, 0)); // Check that it's locked. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_TEST, file_size)); // Move the end. ASSERT_EQ(0, ftruncate(tf.fd, 2*file_size)); // Check that the new section is locked too. ASSERT_EQ(file_size, lseek64(tf.fd, file_size, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_TEST, 2*file_size)); }
TEST(UNISTD_TEST, lockf_smoke) { constexpr off64_t file_size = 32*1024LL; TemporaryFile tf; ASSERT_EQ(0, ftruncate(tf.fd, file_size)); // Lock everything. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_LOCK, file_size)); // Try-lock everything, this should succeed too. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_TLOCK, file_size)); // Check status. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_TEST, file_size)); // Unlock file. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_ULOCK, file_size)); }
void open_archive (struct locarhandle *ah, bool readonly) { struct stat64 st; struct stat64 st2; int fd; struct locarhead head; int retry = 0; size_t prefix_len = output_prefix ? strlen (output_prefix) : 0; char archivefname[prefix_len + sizeof (ARCHIVE_NAME)]; if (output_prefix) memcpy (archivefname, output_prefix, prefix_len); strcpy (archivefname + prefix_len, ARCHIVE_NAME); while (1) { /* Open the archive. We must have exclusive write access. */ fd = open64 (archivefname, readonly ? O_RDONLY : O_RDWR); if (fd == -1) { /* Maybe the file does not yet exist. */ if (errno == ENOENT) { if (readonly) { static const struct locarhead nullhead = { .namehash_used = 0, .namehash_offset = 0, .namehash_size = 0 }; ah->addr = (void *) &nullhead; ah->fd = -1; } else create_archive (archivefname, ah); return; } else error (EXIT_FAILURE, errno, _("cannot open locale archive \"%s\""), archivefname); } if (fstat64 (fd, &st) < 0) error (EXIT_FAILURE, errno, _("cannot stat locale archive \"%s\""), archivefname); if (!readonly && lockf64 (fd, F_LOCK, sizeof (struct locarhead)) == -1) { close (fd); if (retry++ < max_locarchive_open_retry) { struct timespec req; /* Wait for a bit. */ req.tv_sec = 0; req.tv_nsec = 1000000 * (random () % 500 + 1); (void) nanosleep (&req, NULL); continue; } error (EXIT_FAILURE, errno, _("cannot lock locale archive \"%s\""), archivefname); } /* One more check. Maybe another process replaced the archive file with a new, larger one since we opened the file. */ if (stat64 (archivefname, &st2) == -1 || st.st_dev != st2.st_dev || st.st_ino != st2.st_ino) { (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead)); close (fd); continue; } /* Leave the loop. */ break; }
static void enlarge_archive (struct locarhandle *ah, const struct locarhead *head) { struct stat64 st; int fd; struct locarhead newhead; size_t total; void *p; unsigned int cnt, loccnt; struct namehashent *oldnamehashtab; struct locrecent *oldlocrectab; struct locarhandle new_ah; struct oldlocrecent *oldlocrecarray; size_t prefix_len = output_prefix ? strlen (output_prefix) : 0; char archivefname[prefix_len + sizeof (ARCHIVE_NAME)]; char fname[prefix_len + sizeof (ARCHIVE_NAME) + sizeof (".XXXXXX") - 1]; if (output_prefix) memcpy (archivefname, output_prefix, prefix_len); strcpy (archivefname + prefix_len, ARCHIVE_NAME); strcpy (stpcpy (fname, archivefname), ".XXXXXX"); /* Not all of the old file has to be mapped. Change this now this we will have to access the whole content. */ if (fstat64 (ah->fd, &st) != 0) enomap: error (EXIT_FAILURE, errno, _("cannot map locale archive file")); if (st.st_size < ah->reserved) ah->addr = mmap64 (ah->addr, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, ah->fd, 0); else { munmap (ah->addr, ah->reserved); ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, ah->fd, 0); ah->reserved = st.st_size; head = ah->addr; } if (ah->addr == MAP_FAILED) goto enomap; ah->mmaped = st.st_size; /* Create a temporary file in the correct directory. */ fd = mkstemp (fname); if (fd == -1) error (EXIT_FAILURE, errno, _("cannot create temporary file")); /* Copy the existing head information. */ newhead = *head; /* Create the new archive header. The sizes of the various tables should be double from what is currently used. */ newhead.namehash_size = MAX (next_prime (2 * newhead.namehash_used), newhead.namehash_size); if (verbose) printf ("name: size: %u, used: %d, new: size: %u\n", head->namehash_size, head->namehash_used, newhead.namehash_size); newhead.string_offset = (newhead.namehash_offset + (newhead.namehash_size * sizeof (struct namehashent))); /* Keep the string table size aligned to 4 bytes, so that all the struct { uint32_t } types following are happy. */ newhead.string_size = MAX ((2 * newhead.string_used + 3) & -4, newhead.string_size); newhead.locrectab_offset = newhead.string_offset + newhead.string_size; newhead.locrectab_size = MAX (2 * newhead.locrectab_used, newhead.locrectab_size); newhead.sumhash_offset = (newhead.locrectab_offset + (newhead.locrectab_size * sizeof (struct locrecent))); newhead.sumhash_size = MAX (next_prime (2 * newhead.sumhash_used), newhead.sumhash_size); total = (newhead.sumhash_offset + newhead.sumhash_size * sizeof (struct sumhashent)); /* The new file is empty now. */ newhead.namehash_used = 0; newhead.string_used = 0; newhead.locrectab_used = 0; newhead.sumhash_used = 0; /* Write out the header and create room for the other data structures. */ if (TEMP_FAILURE_RETRY (write (fd, &newhead, sizeof (newhead))) != sizeof (newhead)) { int errval = errno; unlink (fname); error (EXIT_FAILURE, errval, _("cannot initialize archive file")); } if (ftruncate64 (fd, total) != 0) { int errval = errno; unlink (fname); error (EXIT_FAILURE, errval, _("cannot resize archive file")); } /* To prepare for enlargements of the mmaped area reserve some address space. */ size_t reserved = RESERVE_MMAP_SIZE; int xflags = 0; if (total < reserved && ((p = mmap64 (NULL, reserved, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0)) != MAP_FAILED)) xflags = MAP_FIXED; else { p = NULL; reserved = total; } /* Map the header and all the administration data structures. */ p = mmap64 (p, total, PROT_READ | PROT_WRITE, MAP_SHARED | xflags, fd, 0); if (p == MAP_FAILED) { int errval = errno; unlink (fname); error (EXIT_FAILURE, errval, _("cannot map archive header")); } /* Lock the new file. */ if (lockf64 (fd, F_LOCK, total) != 0) { int errval = errno; unlink (fname); error (EXIT_FAILURE, errval, _("cannot lock new archive")); } new_ah.mmaped = total; new_ah.addr = p; new_ah.fd = fd; new_ah.reserved = reserved; /* Walk through the hash name hash table to find out what data is still referenced and transfer it into the new file. */ oldnamehashtab = (struct namehashent *) ((char *) ah->addr + head->namehash_offset); oldlocrectab = (struct locrecent *) ((char *) ah->addr + head->locrectab_offset); /* Sort the old locrec table in order of data position. */ oldlocrecarray = alloca (sizeof (*oldlocrecarray) * head->namehash_size); for (cnt = 0, loccnt = 0; cnt < head->namehash_size; ++cnt) if (oldnamehashtab[cnt].locrec_offset != 0) { oldlocrecarray[loccnt].cnt = cnt; oldlocrecarray[loccnt++].locrec = (struct locrecent *) ((char *) ah->addr + oldnamehashtab[cnt].locrec_offset); } qsort (oldlocrecarray, loccnt, sizeof (struct oldlocrecent), oldlocrecentcmp); uint32_t last_locrec_offset = 0; for (cnt = 0; cnt < loccnt; ++cnt) { /* Insert this entry in the new hash table. */ locale_data_t old_data; unsigned int idx; struct locrecent *oldlocrec = oldlocrecarray[cnt].locrec; for (idx = 0; idx < __LC_LAST; ++idx) if (idx != LC_ALL) { old_data[idx].size = oldlocrec->record[idx].len; old_data[idx].addr = ((char *) ah->addr + oldlocrec->record[idx].offset); __md5_buffer (old_data[idx].addr, old_data[idx].size, old_data[idx].sum); } if (cnt > 0 && oldlocrecarray[cnt - 1].locrec == oldlocrec) { const char *oldname = ((char *) ah->addr + oldnamehashtab[oldlocrecarray[cnt - 1].cnt].name_offset); add_alias (&new_ah, ((char *) ah->addr + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset), 0, oldname, &last_locrec_offset); continue; } last_locrec_offset = add_locale (&new_ah, ((char *) ah->addr + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset), old_data, 0); if (last_locrec_offset == 0) error (EXIT_FAILURE, 0, _("cannot extend locale archive file")); } /* Make the file globally readable. */ if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1) { int errval = errno; unlink (fname); error (EXIT_FAILURE, errval, _("cannot change mode of resized locale archive")); } /* Rename the new file. */ if (rename (fname, archivefname) != 0) { int errval = errno; unlink (fname); error (EXIT_FAILURE, errval, _("cannot rename new archive")); } /* Close the old file. */ close_archive (ah); /* Add the information for the new one. */ *ah = new_ah; }