Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

db_open.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1996-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: db_open.c,v 12.13 2005/10/12 17:45:53 bostic Exp $
00008  */
00009 
00010 #include "db_config.h"
00011 
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014 
00015 #include <stdlib.h>
00016 #include <string.h>
00017 #endif
00018 
00019 #include "db_int.h"
00020 #include "dbinc/db_page.h"
00021 #include "dbinc/db_shash.h"
00022 #include "dbinc/db_swap.h"
00023 #include "dbinc/btree.h"
00024 #include "dbinc/crypto.h"
00025 #include "dbinc/hmac.h"
00026 #include "dbinc/fop.h"
00027 #include "dbinc/hash.h"
00028 #include "dbinc/lock.h"
00029 #include "dbinc/log.h"
00030 #include "dbinc/mp.h"
00031 #include "dbinc/qam.h"
00032 #include "dbinc/txn.h"
00033 
00034 /*
00035  * __db_open --
00036  *      DB->open method.
00037  *
00038  * This routine gets called in three different ways:
00039  *
00040  * 1. It can be called to open a file/database.  In this case, subdb will
00041  *    be NULL and meta_pgno will be PGNO_BASE_MD.
00042  * 2. It can be called to open a subdatabase during normal operation.  In
00043  *    this case, name and subname will both be non-NULL and meta_pgno will
00044  *    be PGNO_BASE_MD (also PGNO_INVALID).
00045  * 3. It can be called to open an in-memory database (name == NULL;
00046  *    subname = name).
00047  * 4. It can be called during recovery to open a file/database, in which case
00048  *    name will be non-NULL, subname will be NULL, and meta-pgno will be
00049  *    PGNO_BASE_MD.
00050  * 5. It can be called during recovery to open a subdatabase, in which case
00051  *    name will be non-NULL, subname may be NULL and meta-pgno will be
00052  *    a valid pgno (i.e., not PGNO_BASE_MD).
00053  * 6. It can be called during recovery to open an in-memory database.
00054  *
00055  * PUBLIC: int __db_open __P((DB *, DB_TXN *,
00056  * PUBLIC:     const char *, const char *, DBTYPE, u_int32_t, int, db_pgno_t));
00057  */
00058 int
00059 __db_open(dbp, txn, fname, dname, type, flags, mode, meta_pgno)
00060         DB *dbp;
00061         DB_TXN *txn;
00062         const char *fname, *dname;
00063         DBTYPE type;
00064         u_int32_t flags;
00065         int mode;
00066         db_pgno_t meta_pgno;
00067 {
00068         DB_ENV *dbenv;
00069         int ret;
00070         u_int32_t id;
00071 
00072         dbenv = dbp->dbenv;
00073         id = TXN_INVALID;
00074 
00075         DB_TEST_RECOVERY(dbp, DB_TEST_PREOPEN, ret, fname);
00076 
00077         /*
00078          * If the environment was configured with threads, the DB handle
00079          * must also be free-threaded, so we force the DB_THREAD flag on.
00080          * (See SR #2033 for why this is a requirement--recovery needs
00081          * to be able to grab a dbp using __db_fileid_to_dbp, and it has
00082          * no way of knowing which dbp goes with which thread, so whichever
00083          * one it finds has to be usable in any of them.)
00084          */
00085         if (F_ISSET(dbenv, DB_ENV_THREAD))
00086                 LF_SET(DB_THREAD);
00087 
00088         /* Convert any DB->open flags. */
00089         if (LF_ISSET(DB_RDONLY))
00090                 F_SET(dbp, DB_AM_RDONLY);
00091         if (LF_ISSET(DB_READ_UNCOMMITTED))
00092                 F_SET(dbp, DB_AM_READ_UNCOMMITTED);
00093 
00094         if (txn != NULL)
00095                 F_SET(dbp, DB_AM_TXN);
00096 
00097         /* Fill in the type. */
00098         dbp->type = type;
00099 
00100         /*
00101          * If both fname and subname are NULL, it's always a create, so make
00102          * sure that we have both DB_CREATE and a type specified.  It would
00103          * be nice if this checking were done in __db_open where most of the
00104          * interface checking is done, but this interface (__db_dbopen) is
00105          * used by the recovery and limbo system, so we need to safeguard
00106          * this interface as well.
00107          */
00108         if (fname == NULL) {
00109                 if (dname == NULL) {
00110                         if (!LF_ISSET(DB_CREATE)) {
00111                                 __db_err(dbenv,
00112                             "DB_CREATE must be specified to create databases.");
00113                                 return (ENOENT);
00114                         }
00115 
00116                         F_SET(dbp, DB_AM_INMEM);
00117                         F_SET(dbp, DB_AM_CREATED);
00118 
00119                         if (dbp->type == DB_UNKNOWN) {
00120                                 __db_err(dbenv,
00121                                     "DBTYPE of unknown without existing file");
00122                                 return (EINVAL);
00123                         }
00124 
00125                         if (dbp->pgsize == 0)
00126                                 dbp->pgsize = DB_DEF_IOSIZE;
00127 
00128                         /*
00129                          * If the file is a temporary file and we're
00130                          * doing locking, then we have to create a
00131                          * unique file ID.  We can't use our normal
00132                          * dev/inode pair (or whatever this OS uses
00133                          * in place of dev/inode pairs) because no
00134                          * backing file will be created until the
00135                          * mpool cache is filled forcing the buffers
00136                          * to disk.  Grab a random locker ID to use
00137                          * as a file ID.  The created ID must never
00138                          * match a potential real file ID -- we know
00139                          * it won't because real file IDs contain a
00140                          * time stamp after the dev/inode pair, and
00141                          * we're simply storing a 4-byte value.
00142 
00143                          * !!!
00144                          * Store the locker in the file id structure
00145                          * -- we can get it from there as necessary,
00146                          * and it saves having two copies.
00147                         */
00148                         if (LOCKING_ON(dbenv) && (ret = __lock_id(dbenv,
00149                             (u_int32_t *)dbp->fileid, NULL)) != 0)
00150                                 return (ret);
00151                 } else
00152                         MAKE_INMEM(dbp);
00153 
00154                 /*
00155                  * Normally we would do handle locking here, however, with
00156                  * in-memory files, we cannot do any database manipulation
00157                  * until the mpool is open, so it happens later.
00158                  */
00159         } else if (dname == NULL && meta_pgno == PGNO_BASE_MD) {
00160                 /* Open/create the underlying file.  Acquire locks. */
00161                 if ((ret =
00162                     __fop_file_setup(dbp, txn, fname, mode, flags, &id)) != 0)
00163                         return (ret);
00164         } else {
00165                 if ((ret = __fop_subdb_setup(dbp,
00166                     txn, fname, dname, mode, flags)) != 0)
00167                         return (ret);
00168                 meta_pgno = dbp->meta_pgno;
00169         }
00170 
00171         /*
00172          * If we created the file, set the truncate flag for the mpool.  This
00173          * isn't for anything we've done, it's protection against stupid user
00174          * tricks: if the user deleted a file behind Berkeley DB's back, we
00175          * may still have pages in the mpool that match the file's "unique" ID.
00176          *
00177          * Note that if we're opening a subdatabase, we don't want to set
00178          * the TRUNCATE flag even if we just created the file--we already
00179          * opened and updated the master using access method interfaces,
00180          * so we don't want to get rid of any pages that are in the mpool.
00181          * If we created the file when we opened the master, we already hit
00182          * this check in a non-subdatabase context then.
00183          */
00184         if (dname == NULL && F_ISSET(dbp, DB_AM_CREATED))
00185                 LF_SET(DB_TRUNCATE);
00186 
00187         /* Set up the underlying environment. */
00188         if ((ret = __db_dbenv_setup(dbp, txn, fname, dname, id, flags)) != 0)
00189                 return (ret);
00190 
00191         /* For in-memory databases, we now need to open/create the database. */
00192         if (F_ISSET(dbp, DB_AM_INMEM)) {
00193                 if (dname == NULL)
00194                         ret = __db_new_file(dbp, txn, NULL, NULL);
00195                 else {
00196                         id = TXN_INVALID;
00197                         if ((ret = __fop_file_setup(dbp,
00198                             txn, dname, mode, flags, &id)) == 0 &&
00199                             DBENV_LOGGING(dbenv) && !F_ISSET(dbp, DB_AM_RECOVER)
00200 #if !defined(DEBUG_ROP)
00201             && !F_ISSET(dbp, DB_AM_RDONLY)
00202 #endif
00203                         )
00204                                 ret = __dbreg_log_id(dbp,
00205                                     txn, dbp->log_filename->id, 1);
00206                 }
00207                 if (ret != 0)
00208                         goto err;
00209         }
00210 
00211         switch (dbp->type) {
00212                 case DB_BTREE:
00213                         ret = __bam_open(dbp, txn, fname, meta_pgno, flags);
00214                         break;
00215                 case DB_HASH:
00216                         ret = __ham_open(dbp, txn, fname, meta_pgno, flags);
00217                         break;
00218                 case DB_RECNO:
00219                         ret = __ram_open(dbp, txn, fname, meta_pgno, flags);
00220                         break;
00221                 case DB_QUEUE:
00222                         ret = __qam_open(
00223                             dbp, txn, fname, meta_pgno, mode, flags);
00224                         break;
00225                 case DB_UNKNOWN:
00226                         return (
00227                             __db_unknown_type(dbenv, "__db_dbopen", dbp->type));
00228         }
00229         if (ret != 0)
00230                 goto err;
00231 
00232         DB_TEST_RECOVERY(dbp, DB_TEST_POSTOPEN, ret, fname);
00233 
00234         /*
00235          * Temporary files don't need handle locks, so we only have to check
00236          * for a handle lock downgrade or lockevent in the case of named
00237          * files.
00238          */
00239         if (!F_ISSET(dbp, DB_AM_RECOVER) && (fname != NULL || dname != NULL)
00240             && LOCK_ISSET(dbp->handle_lock)) {
00241                 if (txn != NULL)
00242                         ret = __txn_lockevent(dbenv,
00243                             txn, dbp, &dbp->handle_lock, dbp->lid);
00244                 else if (LOCKING_ON(dbenv))
00245                         /* Trade write handle lock for read handle lock. */
00246                         ret = __lock_downgrade(dbenv,
00247                             &dbp->handle_lock, DB_LOCK_READ, 0);
00248         }
00249 DB_TEST_RECOVERY_LABEL
00250 err:
00251         return (ret);
00252 }
00253 
00254 /*
00255  * __db_get_open_flags --
00256  *      Accessor for flags passed into DB->open call
00257  *
00258  * PUBLIC: int __db_get_open_flags __P((DB *, u_int32_t *));
00259  */
00260 int
00261 __db_get_open_flags(dbp, flagsp)
00262         DB *dbp;
00263         u_int32_t *flagsp;
00264 {
00265         DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->get_open_flags");
00266 
00267         *flagsp = dbp->open_flags;
00268         return (0);
00269 }
00270 
00271 /*
00272  * __db_new_file --
00273  *      Create a new database file.
00274  *
00275  * PUBLIC: int __db_new_file __P((DB *, DB_TXN *, DB_FH *, const char *));
00276  */
00277 int
00278 __db_new_file(dbp, txn, fhp, name)
00279         DB *dbp;
00280         DB_TXN *txn;
00281         DB_FH *fhp;
00282         const char *name;
00283 {
00284         int ret;
00285 
00286         switch (dbp->type) {
00287         case DB_BTREE:
00288         case DB_RECNO:
00289                 ret = __bam_new_file(dbp, txn, fhp, name);
00290                 break;
00291         case DB_HASH:
00292                 ret = __ham_new_file(dbp, txn, fhp, name);
00293                 break;
00294         case DB_QUEUE:
00295                 ret = __qam_new_file(dbp, txn, fhp, name);
00296                 break;
00297         case DB_UNKNOWN:
00298         default:
00299                 __db_err(dbp->dbenv,
00300                     "%s: Invalid type %d specified", name, dbp->type);
00301                 ret = EINVAL;
00302                 break;
00303         }
00304 
00305         DB_TEST_RECOVERY(dbp, DB_TEST_POSTLOGMETA, ret, name);
00306         /* Sync the file in preparation for moving it into place. */
00307         if (ret == 0 && fhp != NULL)
00308                 ret = __os_fsync(dbp->dbenv, fhp);
00309 
00310         DB_TEST_RECOVERY(dbp, DB_TEST_POSTSYNC, ret, name);
00311 
00312 DB_TEST_RECOVERY_LABEL
00313         return (ret);
00314 }
00315 
00316 /*
00317  * __db_init_subdb --
00318  *      Initialize the dbp for a subdb.
00319  *
00320  * PUBLIC: int __db_init_subdb __P((DB *, DB *, const char *, DB_TXN *));
00321  */
00322 int
00323 __db_init_subdb(mdbp, dbp, name, txn)
00324         DB *mdbp, *dbp;
00325         const char *name;
00326         DB_TXN *txn;
00327 {
00328         DBMETA *meta;
00329         DB_MPOOLFILE *mpf;
00330         int ret, t_ret;
00331 
00332         ret = 0;
00333         if (!F_ISSET(dbp, DB_AM_CREATED)) {
00334                 /* Subdb exists; read meta-data page and initialize. */
00335                 mpf = mdbp->mpf;
00336                 if  ((ret = __memp_fget(mpf, &dbp->meta_pgno, 0, &meta)) != 0)
00337                         goto err;
00338                 ret = __db_meta_setup(mdbp->dbenv, dbp, name, meta, 0, 0);
00339                 if ((t_ret = __memp_fput(mpf, meta, 0)) != 0 && ret == 0)
00340                         ret = t_ret;
00341                 /*
00342                  * If __db_meta_setup found that the meta-page hadn't
00343                  * been written out during recovery, we can just return.
00344                  */
00345                 if (ret == ENOENT)
00346                         ret = 0;
00347                 goto err;
00348         }
00349 
00350         /* Handle the create case here. */
00351         switch (dbp->type) {
00352         case DB_BTREE:
00353         case DB_RECNO:
00354                 ret = __bam_new_subdb(mdbp, dbp, txn);
00355                 break;
00356         case DB_HASH:
00357                 ret = __ham_new_subdb(mdbp, dbp, txn);
00358                 break;
00359         case DB_QUEUE:
00360                 ret = EINVAL;
00361                 break;
00362         case DB_UNKNOWN:
00363         default:
00364                 __db_err(dbp->dbenv,
00365                     "Invalid subdatabase type %d specified", dbp->type);
00366                 return (EINVAL);
00367         }
00368 
00369 err:    return (ret);
00370 }
00371 
00372 /*
00373  * __db_chk_meta --
00374  *      Take a buffer containing a meta-data page and check it for a valid LSN,
00375  *      checksum (and verify the checksum if necessary) and possibly decrypt it.
00376  *
00377  *      Return 0 on success, >0 (errno) on error, -1 on checksum mismatch.
00378  *
00379  * PUBLIC: int __db_chk_meta __P((DB_ENV *, DB *, DBMETA *, int));
00380  */
00381 int
00382 __db_chk_meta(dbenv, dbp, meta, do_metachk)
00383         DB_ENV *dbenv;
00384         DB *dbp;
00385         DBMETA *meta;
00386         int do_metachk;
00387 {
00388         DB_LSN cur_lsn, swap_lsn;
00389         int is_hmac, ret, swapped;
00390         u_int32_t magic, orig_chk;
00391         u_int8_t *chksum;
00392 
00393         ret = 0;
00394         swapped = 0;
00395 
00396         if (FLD_ISSET(meta->metaflags, DBMETA_CHKSUM)) {
00397                 if (dbp != NULL)
00398                         F_SET(dbp, DB_AM_CHKSUM);
00399 
00400                 is_hmac = meta->encrypt_alg == 0 ? 0 : 1;
00401                 chksum = ((BTMETA *)meta)->chksum;
00402 
00403                 /*
00404                  * If we need to swap, the checksum function overwrites the
00405                  * original checksum with 0, so we need to save a copy of the
00406                  * original for swapping later.
00407                  */
00408                 orig_chk = *(u_int32_t *)chksum;
00409 
00410                 /*
00411                  * We cannot add this to __db_metaswap because that gets done
00412                  * later after we've verified the checksum or decrypted.
00413                  */
00414                 if (do_metachk) {
00415                         swapped = 0;
00416 chk_retry:              if ((ret = __db_check_chksum(dbenv,
00417                             (DB_CIPHER *)dbenv->crypto_handle, chksum, meta,
00418                             DBMETASIZE, is_hmac)) != 0) {
00419                                 if (is_hmac || swapped)
00420                                         return (ret);
00421 
00422                                 M_32_SWAP(orig_chk);
00423                                 swapped = 1;
00424                                 *(u_int32_t *)chksum = orig_chk;
00425                                 goto chk_retry;
00426                         }
00427                 }
00428         } else if (dbp != NULL)
00429                 F_CLR(dbp, DB_AM_CHKSUM);
00430 
00431 #ifdef HAVE_CRYPTO
00432         ret = __crypto_decrypt_meta(dbenv, dbp, (u_int8_t *)meta, do_metachk);
00433 #endif
00434 
00435         /* Now that we're decrypted, we can check LSN. */
00436         if (LOGGING_ON(dbenv)) {
00437                 /*
00438                  * This gets called both before and after swapping, so we
00439                  * need to check ourselves.  If we already swapped it above,
00440                  * we'll know that here.
00441                  */
00442 
00443                 swap_lsn = meta->lsn;
00444                 magic = meta->magic;
00445 lsn_retry:
00446                 if (swapped) {
00447                         M_32_SWAP(swap_lsn.file);
00448                         M_32_SWAP(swap_lsn.offset);
00449                         M_32_SWAP(magic);
00450                 }
00451                 switch (magic) {
00452                 case DB_BTREEMAGIC:
00453                 case DB_HASHMAGIC:
00454                 case DB_QAMMAGIC:
00455                 case DB_RENAMEMAGIC:
00456                         break;
00457                 default:
00458                         if (swapped)
00459                                 return (EINVAL);
00460                         swapped = 1;
00461                         goto lsn_retry;
00462                 }
00463                 if (!IS_REP_CLIENT(dbenv) &&
00464                     !IS_NOT_LOGGED_LSN(swap_lsn) && !IS_ZERO_LSN(swap_lsn)) {
00465                         /* Need to do check. */
00466                         if ((ret = __log_current_lsn(dbenv,
00467                             &cur_lsn, NULL, NULL)) != 0)
00468                                 return (ret);
00469                         if (log_compare(&swap_lsn, &cur_lsn) > 0) {
00470                                 __db_err(dbenv,
00471                         "file %s (meta pgno = %lu) has LSN [%lu][%lu].",
00472                                     dbp->fname == NULL
00473                                     ? "unknown" : dbp->fname,
00474                                     (u_long)dbp->meta_pgno,
00475                                     (u_long)swap_lsn.file,
00476                                     (u_long)swap_lsn.offset);
00477                                 __db_err(dbenv, "end of log is [%lu][%lu]",
00478                                     (u_long)cur_lsn.file,
00479                                     (u_long)cur_lsn.offset);
00480                                 return (EINVAL);
00481                         }
00482                 }
00483         }
00484         return (ret);
00485 }
00486 
00487 /*
00488  * __db_meta_setup --
00489  *
00490  * Take a buffer containing a meta-data page and figure out if it's
00491  * valid, and if so, initialize the dbp from the meta-data page.
00492  *
00493  * PUBLIC: int __db_meta_setup __P((DB_ENV *,
00494  * PUBLIC:     DB *, const char *, DBMETA *, u_int32_t, int));
00495  */
00496 int
00497 __db_meta_setup(dbenv, dbp, name, meta, oflags, do_metachk)
00498         DB_ENV *dbenv;
00499         DB *dbp;
00500         const char *name;
00501         DBMETA *meta;
00502         u_int32_t oflags;
00503         int do_metachk;
00504 {
00505         u_int32_t flags, magic;
00506         int ret;
00507 
00508         ret = 0;
00509 
00510         /*
00511          * Figure out what access method we're dealing with, and then
00512          * call access method specific code to check error conditions
00513          * based on conflicts between the found file and application
00514          * arguments.  A found file overrides some user information --
00515          * we don't consider it an error, for example, if the user set
00516          * an expected byte order and the found file doesn't match it.
00517          */
00518         F_CLR(dbp, DB_AM_SWAP | DB_AM_IN_RENAME);
00519         magic = meta->magic;
00520 
00521 swap_retry:
00522         switch (magic) {
00523         case DB_BTREEMAGIC:
00524         case DB_HASHMAGIC:
00525         case DB_QAMMAGIC:
00526         case DB_RENAMEMAGIC:
00527                 break;
00528         case 0:
00529                 /*
00530                  * The only time this should be 0 is if we're in the
00531                  * midst of opening a subdb during recovery and that
00532                  * subdatabase had its meta-data page allocated, but
00533                  * not yet initialized.
00534                  */
00535                 if (F_ISSET(dbp, DB_AM_SUBDB) && ((IS_RECOVERING(dbenv) &&
00536                     F_ISSET((DB_LOG *) dbenv->lg_handle, DBLOG_FORCE_OPEN)) ||
00537                     meta->pgno != PGNO_INVALID))
00538                         return (ENOENT);
00539 
00540                 goto bad_format;
00541         default:
00542                 if (F_ISSET(dbp, DB_AM_SWAP))
00543                         goto bad_format;
00544 
00545                 M_32_SWAP(magic);
00546                 F_SET(dbp, DB_AM_SWAP);
00547                 goto swap_retry;
00548         }
00549 
00550         /*
00551          * We can only check the meta page if we are sure we have a meta page.
00552          * If it is random data, then this check can fail.  So only now can we
00553          * checksum and decrypt.  Don't distinguish between configuration and
00554          * checksum match errors here, because we haven't opened the database
00555          * and even a checksum error isn't a reason to panic the environment.
00556          */
00557         if ((ret = __db_chk_meta(dbenv, dbp, meta, do_metachk)) != 0) {
00558                 if (ret == -1)
00559                         __db_err(dbenv,
00560                             "%s: metadata page checksum error", name);
00561                 goto bad_format;
00562         }
00563 
00564         switch (magic) {
00565         case DB_BTREEMAGIC:
00566                 if (dbp->type != DB_UNKNOWN &&
00567                     dbp->type != DB_RECNO && dbp->type != DB_BTREE)
00568                         goto bad_format;
00569 
00570                 flags = meta->flags;
00571                 if (F_ISSET(dbp, DB_AM_SWAP))
00572                         M_32_SWAP(flags);
00573                 if (LF_ISSET(BTM_RECNO))
00574                         dbp->type = DB_RECNO;
00575                 else
00576                         dbp->type = DB_BTREE;
00577                 if ((oflags & DB_TRUNCATE) == 0 && (ret =
00578                     __bam_metachk(dbp, name, (BTMETA *)meta)) != 0)
00579                         return (ret);
00580                 break;
00581         case DB_HASHMAGIC:
00582                 if (dbp->type != DB_UNKNOWN && dbp->type != DB_HASH)
00583                         goto bad_format;
00584 
00585                 dbp->type = DB_HASH;
00586                 if ((oflags & DB_TRUNCATE) == 0 && (ret =
00587                     __ham_metachk(dbp, name, (HMETA *)meta)) != 0)
00588                         return (ret);
00589                 break;
00590         case DB_QAMMAGIC:
00591                 if (dbp->type != DB_UNKNOWN && dbp->type != DB_QUEUE)
00592                         goto bad_format;
00593                 dbp->type = DB_QUEUE;
00594                 if ((oflags & DB_TRUNCATE) == 0 && (ret =
00595                     __qam_metachk(dbp, name, (QMETA *)meta)) != 0)
00596                         return (ret);
00597                 break;
00598         case DB_RENAMEMAGIC:
00599                 F_SET(dbp, DB_AM_IN_RENAME);
00600 
00601                 /* Copy the file's ID. */
00602                 memcpy(dbp->fileid, ((DBMETA *)meta)->uid, DB_FILE_ID_LEN);
00603 
00604                 break;
00605         default:
00606                 goto bad_format;
00607         }
00608         return (0);
00609 
00610 bad_format:
00611         if (F_ISSET(dbp, DB_AM_RECOVER))
00612                 ret = ENOENT;
00613         else
00614                 __db_err(dbenv, "%s: unexpected file type or format", name);
00615         return (ret == 0 ? EINVAL : ret);
00616 }

Generated on Sun Dec 25 12:14:20 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2